2016-10-06 10 views
-2

私はいくつかのソケットコードを書いており、いくつかのパラメータに基づいて、私はIPv4またはIPv6のいずれかを使用しています。そのために私はこのようなコードを持っています:memcpyが動作しているときにreinterpret_castが失敗するのはなぜですか?

struct sockaddr final_addr; 
... 
struct sockaddr_in6 addr6; 
... 
memcpy(&final_addr, &addr6, size); 
... 
bind(fd, &final_addr, size); 

これは問題ありません。私は(私の最初のアイデアだった)

struct sockaddr final_addr; 
... 
struct sockaddr_in6 addr6; 
... 
final_addr = *reinterpret_cast<struct sockaddr*>(&addr6); 
... 
bind(fd, &final_addr, size); 

を行う場合は、それはCannot assign requested addressエラーでbindに失敗します。

IPv4のsockaddr_inに切り替えると、この不正なコードが正常に機能することに注意してください。

ここでは何が起こっていますか? をsockaddrと再解釈できないのはなぜですか?

+0

'sockaddr'は' sockaddr_in'(IPv4)の又は 'sockaddr_in6'(IPv6)をバックアップのいずれか不透明ポインタです。 'sin_family'フィールドの他に、これらの構造はまったく異なります。 'reinterpret_cast'と' memcpy() 'はどちらも正しく変換されません。 –

+0

sockaddr_in6にconstキーワードがありますか? – BenPen

+1

私はあなたが誤解していると思います。 Iveは初期化コードの大部分を削除しました。コードはmemcpyを使ってうまく動作します(ソケットは正しくバインドされ、正しく動作します)。しかし、reinterpret_castで失敗します。 – freakish

答えて

2

コードsizeの最初のバージョンでsizeof(addr6)は(あなたがコメントで述べたように)、その後、コードの最初のバージョンは、データのsizeof(struct sockaddr_in6)バイトをコピーするmemcpyを使用している場合。

コードの2番目のバージョンでは、struct sockaddrという通常の割り当てを使用して、コピーするのはsizeof(struct sockaddr)バイトのみです。

sizeof(struct sockaddr)sizeof(struct sockaddr_in6)より小さいため、これら2つのコードサンプルが異なります。

最初のバージョンでは、そのmemcpyの受信者オブジェクトはstruct sockaddrタイプです。つまり、コピーされたバイト数よりも小さいことに注意してください。メモリオーバーランが発生し、隣接するメモリロケーションに格納されている他のデータが壊れてしまいます。このコードは偶然にしか機能しません。私。このビットが "機能する"場合、他のコード(現在のデータに頼っているコード)が失敗する可能性があります。

+0

「作品」は、あなたが書いたことを意味します。以前に割り当てられたスタックに上書きしてしまいましたか?十分に悪い場合には、あなたのリターンコードアドレスのすべてに書き込むでしょう、そうですか? – BenPen

+0

また、あなたも争奪されることになります。 – BenPen

1

sockaddrがからのデータを保持するのに十分な大きさではありません。最初のコード例は、送信元アドレスのデータが満杯にコピーされている、とあなたはbind()に完全なアドレスを渡しているが、あなたはまた、コピー中にスタックメモリをゴミ箱に移動しているという理由だけで「働きます」。それは割り当て中にアドレスデータを切り捨てているが、それはもうスタックメモリを中傷されていないため、2番目のコード例では動作しません。

sockaddrsockaddr_inのデータを保持するのに十分な大きさなので、どちらのコード例もIPv6では正しく動作しますが、どちらも問題ありません。

final_addrを確保するため

sockaddr_inまたはのいずれかからのデータを保持するのに十分な大きさである、それはどのsockaddr_...構造体型からのデータを保持するのに十分な大きさであることが保証され、代わりにsockaddr_storageとして宣言する必要があります

struct sockaddr_storage final_addr; 
int size; 

if (use IPv6) 
{ 
    struct sockaddr_in6 addr6; 
    // populate addr6 as needed... 

    memcpy(&final_addr, &addr6, sizeof(addr6)); 
    or 
    *reinterpret_cast<struct sockaddr_in6*>(&final_addr) = addr6; 

    size = sizeof(addr6); 
} 
else 
{ 
    struct sockaddr_in addr4; 
    // populate addr4 as needed... 

    memcpy(&final_addr, &addr4, sizeof(addr4)); 
    or 
    *reinterpret_cast<struct sockaddr_in*>(&final_addr) = addr4; 

    size = sizeof(addr4); 
} 

bind(fd, reinterpret_cast<struct sockaddr*>(&final_addr), size); 

より良いオプションは、あなたに適したsockaddr_...メモリ・ブロックを作成するためにgetaddrinfo()または同等品を使用することです:

struct addrinfo hints; 
memset(&hints, 0, sizeof(hints)); 

hints.ai_flags = AI_NUMERICHOST; 
hints.ai_family = AF_UNSPEC; 

struct addrinfo *addr = NULL; 

if (getaddrinfo("ip address here", "port here", &hints, &addr) == 0) 
{ 
    bind(fd, addr->ai_addr, addr->ai_addrlen); 
    freeaddrinfo(addr); 
} 

あるいは:

struct addrinfo hints; 
memset(&hints, 0, sizeof(hints)); 

hints.ai_flags = AI_PASSIVE; 
hints.ai_family = AF_UNSPEC; 
hints.ai_socktype = ...; // SOCK_STREAM, SOCK_DGRAM, etc... 
hints.ai_protocol = ...; // IPPROTO_TCP, IPPROTO_UDP, etc... 

struct addrinfo *addrs = NULL; 

if (getaddrinfo(NULL, "port here", &hints, &addrs) == 0) 
{ 
    for (struct addrinfo *addr = addrs; addr != NULL; addr = addr->ai_next) 
    { 
     int fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); 
     if (fd != -1) 
     { 
      bind(fd, addr->ai_addr, addr->ai_addrlen); 
      // save fd somewhere for later use 
      ... 
     } 
    } 
    freeaddrinfo(addrs); 
} 
関連する問題