#はじめに
iOSでもARPしたい!
そう感じられたことはありませんか?
…と思ってネットでいろいろ見ていたのですが、日本語での情報があまり見られなかったので、少し書いてみることにしました。
自分でパケットを作って、ARPしてみるのもいいのかもしれませんが、今回はFreeBSDのarp
コマンドのソースコードを用いて、お手軽に行ってみようと思います。
海外の情報だと、Appleが利用しているFreeBSDのソースコードを利用するという感じでした。
コピーライトが2つ載っていて、ソースコードの又貸し状態です。
#arp
のソースコード
まず、FreeBSDのSVNからソースコードを入手する必要があります。
必要なのは、arp.c
とroute.h
です。
svn派ではなくgit派なので、httpでダウンロードします。
URLは次の2つになります。
https://svnweb.freebsd.org/base/stable/10/usr.sbin/arp/arp.c?view=co
https://svnweb.freebsd.org/base/stable/10/sys/net/route.h?view=co
#不要な部分の削除
実際のarp
コマンドはARPテーブルへの追加や削除が行えますが、実機でそれを行ったら、リジェクト直行便な気がしますので、関連する部分を念のために削除します。
また、標準出力ではなく、struct ether_addr
として取得したいので、出力用の関数なども削ってしまします。
これで、必要なヘッダーファイルを大幅に減らせます。
結局、残す関数は
static struct sockaddr_in *getaddr(char *host)
static int search(u_long addr, action_fn *action)
の2つになりました。
そしてstatic
を外します。
また、search関数でstruct ether_addr
のポインタを渡すように宣言と定義を変更して
struct sockaddr_in *getaddr(char *host)
int search(u_long addr, struct ether_addr *hwaddr)
となります。
#定義の移動
関数を外部から使いたいので、arp.h
を作成してそちらにいくつかの定義を移します。
#include "TargetConditionals.h"
#include <net/ethernet.h>
#include <net/if_dl.h>
#if TARGET_OS_SIMULATOR
#include <net/route.h>
#else
#include "route.h"
#endif
typedef void (action_fn)(struct sockaddr_dl *sdl,
struct sockaddr_in *s_in, struct rt_msghdr *rtm);
struct sockaddr_in *getaddr(char *host);
int search(u_long addr, struct ether_addr *hwaddr);
実は、route.hはiOS SimulatorのSDKには含まれており、自前で用意するとコンパイルが通らなくなるので、定数で分岐します。
分岐に用いるTARGET_OS_SIMULATOR
はTargetConditionals.h
で定義されているので、忘れずに#include
してあげてください。
#ソースコードの修正
arp.h
で、先ほどダウンロードしたroute.h
を#include
してますが、iOSには無いヘッダーファイルを参照しているので、修正します。
まず
#include <sys/counter.h>
を
#include <sys/socket.h>
#include <sys/types.h>
に変更します。
続いて、中ほどにある
#include <net/radix.h>
を削除します。不要です。
次に、arp.c
に戻り、不要な#include
と定義を削除します。
残す#include
は
#include <sys/sysctl.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
の6つになりました。
必要な定義はarp.h
に移しましたので、ほかの定義はばっさり全削除です。
スマートです。
また、iOS SimulatorのSDKにはSA_SIZE
の定義がないと怒られるので、FreeBSDのroute.h
を参考にしてarp.c
に追加しておきます。
#ifndef SA_SIZE
#define SA_SIZE(sa) \
((!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
sizeof(long) : \
1 + ((((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1)))
#endif
最後に、関数本体の修正を行います。
まず、エラー出力を取り除きます。
warnx
関数の呼び出しを削除し、err
関数の呼び出しをreturn 0
などに置換します。
インタフェースの指定などはしないのでsearch
関数から、
if (rifname && if_indextoname(sdl->sdl_index, ifname) &&
strcmp(ifname, rifname))
continue;
のコードとchar ifname[IF_NAMESIZE];
の定義を削除します。
また、search
関数ではstruct ether_addr
を返すので、
(*action)(sdl, sin2, rtm);
を
memcpy(hwaddr, LLADDR(sdl), sdl->sdl_alen);
と修正します。
#使い方
あとは、ARPしたいコードで、関数定義を行ったarp.h
を#include
して呼び出すだけです。
ついでに、文字列からin_addr_t
を生成してくれるinet_addr
関数を定義したarpa/inet.h
を#include
すると便利です。
IPアドレスが格納された文字列変数ip
から、MACアドレスを格納した文字列変数mac
を取得するコードは次のようになります。
struct ether_addr hw_addr;
if (!search(inet_addr([ip cStringUsingEncoding:NSASCIIStringEncoding]), &hw_addr)) {
return nil;
}
NSMutableString *mac = [[NSMutableString alloc] init];
for (int i = 0; i < sizeof(hw_addr.octet); i++) {
if (i > 0) {
[mac appendString:@":"];
}
[mac appendFormat:@"%02X", hw_addr.octet[i]];
}
arp.c
には関数getaddr
も残してあるので、ホスト名を解決してMACアドレスを参照するといったこともできます。
#さいごに
FreeBSDの4条項BSDライセンスに注意して利用してください。
この記事で示したコードはご自由に使っていただいて大丈夫です。ただし、一切の責任を取りかねますのでご了承ください。