首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >使用Ruby和ioctl编程获取wifi BSSID

使用Ruby和ioctl编程获取wifi BSSID
EN

Stack Overflow用户
提问于 2013-10-21 19:51:55
回答 1查看 1.6K关注 0票数 2

使用Getting essid via ioctl in ruby作为模板,我希望获得BSSID,而不是ESSID。然而,作为一个C开发人员,有一些事情我不明白。

我到目前为止所拥有的不起作用的东西:( .

注意到我有点困惑,因为根据wireless.h中的一些评论,我觉得BSSID只能是通过ioctl设置的set。但是,get的ioctl是存在的。除了我对更中间的C型语言(结构,联合,等等)几乎完全缺乏理解之外,我根本不知道。

代码语言:javascript
代码运行次数:0
运行
复制
def _get_bssid(interface)
    # Copied from wireless.h
    # supposing a 16 byte address and 32 byte buffer but I'm totally 
    # guessing here.
    iwreq = [interface, '' * 48,0].pack('a*pI') 
    sock = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)

    # from wireless.h
    # SIOCGIWAP 0x8B15      /* get access point MAC addresses */
    sock.ioctl('0x8B15', iwreq) # always get an error: Can't convert string to Integer

    puts iwreq.inspect
end

因此,同时,我使用wpa_cli方法获取BSSID,但我更愿意使用IOCTL:

代码语言:javascript
代码运行次数:0
运行
复制
def _wpa_status(interface)
    wpa_data = nil

    unless interface.nil?
        # need to write a method to get the src_sock_path 
        # programmatically. Fortunately, for me
        # this is going to be the correct sock path 99% of the time.
        # Ideas to get programmatically would be:
        # parse wpa_supplicant.conf
        # check process table | grep wpa_suppl | parse arguments
        src_sock_path  = '/var/run/wpa_supplicant/' + interface
    else
        return nil
    end

    client_sock_path = '/var/run/hwinfo_wpa'

    # open Domain socket
    socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM, 0)

    begin
        # bind client domain socket
        socket.bind(Socket.pack_sockaddr_un(client_sock_path))

        # connect to server with our client socket
        socket.connect(Socket.pack_sockaddr_un(src_sock_path))

        # send STATUS command
        socket.send('STATUS', 0)

        # receive 1024 bytes (totally arbitrary value)
        # split lines by \n
        # store in variable wpa_data.
        wpa_data = socket.recv(1024)
    rescue => e
        $stderr.puts 'WARN: unable to gather wpa data: ' + e.inspect
    end
    # close or next time we attempt to read it will fail.
    socket.close

    begin
        # remove the domain socket file for the client
        File.unlink(client_sock_path)
    rescue => e
        $stderr.puts 'WARN: ' + e.inspect
    end

    unless wpa_data.nil?
        @wifis = Hash[wpa_data.split(/\n/).map\
                 {|line|
                    # first, split into pairs delimited by '='
                    key,value = line.split('=')

                    # if key is camel-humped then put space in front
                    # of capped letter
                    if key =~ /[a-z][A-Z]/
                        key.gsub!(/([a-z])([A-Z])/,'\\1_\\2')
                    end

                    # if key is "id" then rename it.
                    key.eql?('id') && key = 'wpa_id'

                    # fix key so that it can be used as a table name
                    # by replacing spaces with underscores
                    key.gsub!(' ','_')

                    # lower case it.
                    key.downcase!

                    [key,value]
                 }]
    end
end

编辑:到目前为止还没有人能够回答这个问题。我想我更喜欢wpa方法,因为我从它得到了更多的数据。尽管如此,我想做的一个调用是,如果有人使用wpa代码,请注意,读取wlan套接字需要升级的权限。

编辑^2(完整的代码片段):由于@dasup,我能够重新分析类,从而使用system正确地提取bssid和essids。(YMMV给出了Linux发行版的实现、历史和任何其他可能的不稳定因素--不过,下面的代码片段可以用于3.2和3.7内核。)

代码语言:javascript
代码运行次数:0
运行
复制
require 'socket'

class Wpa
    attr_accessor :essid, :bssid, :if

    def initialize(interface)
        @if = interface

        puts 'essid: ' + _get_essid.inspect
        puts 'bssid: ' + _get_bssid.inspect
    end

    def _get_essid
        # Copied from wireless.h
        iwreq = [@if, " " * 32, 32, 0 ].pack('a16pII')

        sock = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
        sock.ioctl(0x8B1B, iwreq)

        @essid = iwreq.unpack('@16p').pop.strip
    end 

    def _get_bssid
        # Copied from wireless.h
        # supposing a 16 byte address and 32 byte buffer but I'm totally 
        # guessing here.
        iwreq = [@if, "\0" * 32].pack('a16a32') 
        sock = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)

        # from wireless.h
        # SIOCGIWAP 0x8B15      /* get access point MAC addresses */
        sock.ioctl(0x8B15, iwreq) # always get an error: Can't convert string to Integer

        @bssid = iwreq.unpack('@18H2H2H2H2H2H2').join(':')
    end
end

h = Wpa.new('wlan0')
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-02-25 00:50:53

我不太熟悉Ruby,但我发现了两个错误:

  • SIOCGIWAP的十六进制数应该不带引号/滴答。
  • 数据缓冲区的初始化以接口名称后的尾随字节结束(使用gdb调试)。下面给出的初始化是可行的。

请注意,如果任何数据结构或常量发生更改(IFNAMSIZ、sa_family、sockaddr等),您的代码就会中断。然而,我认为这种变化不太可能在短期内发生。

代码语言:javascript
代码运行次数:0
运行
复制
require 'socket'

def _get_bssid(interface)
    # Copied from wireless.h
    # supposing a 16 byte address and 32 byte buffer but I'm totally 
    # guessing here.
    iwreq = [interface, "\0" * 32].pack('a16a32') 
    sock = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)

    # from wireless.h
    # SIOCGIWAP 0x8B15      /* get access point MAC addresses */
    sock.ioctl(0x8B15, iwreq) # always get an error: Can't convert string to Integer

    puts iwreq.inspect
end

您将通过以下方法获得数组/缓冲区:

  • 您发送的接口名称,其中填充了0x00字节,其总长度为16字节。
  • 后面跟着一个struct sockaddr,即两个字节的标识符0x01 0x00 (来自ARPHRD_ETHER?)然后是用0x00字节填充的BSSID,总长度为14个字节。

祝好运!

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19503446

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档