4

iOS 用の単純な traceroute を実装しようとしています。シミュレーターまたはデバイスでアプリケーションを実行すると、CLI traceroute が 14 個のルーターすべてを検出したときに、最初のルーターがいくつか (6-7) しか検出されないことを除いて、すべて正常に動作しているようです。

const char *c = "www.gmail.com";
struct hostent *host_entry = gethostbyname(c);
char *ip_addr;
ip_addr = inet_ntoa(*((struct in_addr *)host_entry->h_addr_list[0]));
struct sockaddr_in destination, fromAddr;
int recv_sock;
int send_sock;

// Creting Sockets///

if ((recv_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)) <
    0) // using UDP socket.
{
  NSLog(@"Could not cretae recv_sock.\n");
}
if ((send_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
  NSLog(@"Could not cretae send_sock.\n");
}
memset(&destination, 0, sizeof(destination));
destination.sin_family = AF_INET;
destination.sin_addr.s_addr = inet_addr(ip_addr);
destination.sin_port = htons(80);
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 10000;
setsockopt(recv_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,
           sizeof(struct timeval));
char *cmsg = "GET / HTTP/1.1\r\n\r\n";
int max_ttl = 20;
int num_attempts = 5;
socklen_t n = sizeof(fromAddr);
char buf[100];
for (int ttl = 1; ttl <= max_ttl; ttl++) {
  memset(&fromAddr, 0, sizeof(fromAddr));
  if (setsockopt(send_sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0)
    NSLog(@"error in setsockopt\n");
  for (int try = 0; try < num_attempts; try ++) {
    if (sendto(send_sock, cmsg, sizeof(cmsg), 0,
               (struct sockaddr *)&destination,
               sizeof(destination)) != sizeof(cmsg))
      NSLog(@"error in send to...\n@");
    int res = 0;
    if ((res = recvfrom(recv_sock, buf, 100, 0, (struct sockaddr *)&fromAddr,
                        &n)) < 0) {
      NSLog(@"an error: %s; recvfrom returned %d\n", strerror(errno), res);
    } else {
      char display[16] = {0};
      inet_ntop(AF_INET, &fromAddr.sin_addr.s_addr, display, sizeof(display));
      NSLog(@"Received packet from%s for TTL=%d\n", display, ttl);
      break;
    }
  }
}

送信ソケットをバインドしようとしましたが、結果は同じで、iOS で Sock_raw を使用できません。Macで実行しようとしましたが、同じ結果が得られました。表示されるエラーは、「リソースが一時的に利用できません。」のためにrecvfrom()。何故ですか?どうすれば修正できますか?

4

1 に答える 1

3

EAGAINエラー(「リソースが一時的に利用できません;」文字列を生成する)は、受信ソケットのタイムアウトによって発生する可能性があります。

この行では、読み取りタイムアウトとして10000マイクロ秒(これは本当に短いIMHO)として設定したので...

struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 10000;
setsockopt(recv_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval));

...道が長いほど(つまり、通過する必要のあるルーターの数)、この状況で不利益を被る可能性が高くなる可能性があります。

タイムアウト値を上げて、改善されたかどうかをお知らせください。

編集

Linuxでソースコードを試してみたところ、2種類の問題に気づきました。

  • 上記のように:タイムアウト
  • 80ポートの問題

タイムアウトを上げて80とは異なるポートを使用し(私の場合はudpメッセージを40000ポートに送信しました)、tracerouteコマンドと同じようにすべてのホップを取得しました。

なぜこの動作が発生するのかわかりません。ある種の「悪意のあるパケットアラームの可能性」が、それを破棄するルーターによってトリガーされる可能性があります

さらに編集

このリンクを見てください:man traceroute

利用可能な方法のリストセクションでは、必要なことを達成するための多くの方法を見つけることができます。あなたの方法はデフォルトの方法に似ており、次のように述べています。

プローブパケットは、いわゆる「ありそうもない」宛先ポートを持つudpデータグラムです。最初のプローブの「ありそうもない」ポートは33434で、次のプローブごとに1ずつインクリメントされます。ポートは未使用であると予想されるため、宛先ホストは通常​​、最終応答として「icmpunreachport」を返します。(ただし、一部のアプリケーションがそのようなポートをリッスンするとどうなるかは誰にもわかりません)。

したがって、一般的なLinux tracerouteの動作を完全にエミュレートする必要がある場合は、TTLが増加するたびに(または応答IMHOを取得できないたびに)、宛先ポートを1つ増やす必要があります。

たぶん、ルーターが後者をリッスンしているために、コマンドが特定のポートで機能しないことがあります(Linuxのマニュアルで提案されており、太字で下線が引かれています)。

于 2013-01-13T15:30:12.873 に答える