1/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 * 3 * Permission is hereby granted, free of charge, to any person obtaining a copy 4 * of this software and associated documentation files (the "Software"), to 5 * deal in the Software without restriction, including without limitation the 6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 * sell copies of the Software, and to permit persons to whom the Software is 8 * furnished to do so, subject to the following conditions: 9 * 10 * The above copyright notice and this permission notice shall be included in 11 * all copies or substantial portions of the Software. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 * IN THE SOFTWARE. 20 */ 21 22#include "uv.h" 23#include "task.h" 24 25#include <stdio.h> 26#include <stdlib.h> 27#include <string.h> 28 29#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) 30#include <sys/sysctl.h> 31#endif 32 33#define CHECK_HANDLE(handle) \ 34 ASSERT((uv_udp_t*)(handle) == &server \ 35 || (uv_udp_t*)(handle) == &client \ 36 || (uv_timer_t*)(handle) == &timeout) 37 38#define CHECK_REQ(req) \ 39 ASSERT((req) == &req_); 40 41static uv_udp_t client; 42static uv_udp_t server; 43static uv_udp_send_t req_; 44static char data[10]; 45static uv_timer_t timeout; 46 47static int send_cb_called; 48static int recv_cb_called; 49static int close_cb_called; 50static uint16_t client_port; 51 52#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) 53static int can_ipv6_ipv4_dual(void) { 54 int v6only; 55 size_t size = sizeof(int); 56 57 if (sysctlbyname("net.inet6.ip6.v6only", &v6only, &size, NULL, 0)) 58 return 0; 59 60 return v6only != 1; 61} 62#endif 63 64 65static void alloc_cb(uv_handle_t* handle, 66 size_t suggested_size, 67 uv_buf_t* buf) { 68 static char slab[65536]; 69 CHECK_HANDLE(handle); 70 buf->base = slab; 71 buf->len = sizeof(slab); 72} 73 74 75static void close_cb(uv_handle_t* handle) { 76 CHECK_HANDLE(handle); 77 close_cb_called++; 78} 79 80 81static void send_cb(uv_udp_send_t* req, int status) { 82 CHECK_REQ(req); 83 CHECK_HANDLE(req->handle); 84 ASSERT(status == 0); 85 send_cb_called++; 86} 87 88static int is_from_client(const struct sockaddr* addr) { 89 const struct sockaddr_in6* addr6; 90 char dst[256]; 91 int r; 92 93 /* Debugging output, and filter out unwanted network traffic */ 94 if (addr != NULL) { 95 ASSERT(addr->sa_family == AF_INET6); 96 addr6 = (struct sockaddr_in6*) addr; 97 r = uv_inet_ntop(addr->sa_family, &addr6->sin6_addr, dst, sizeof(dst)); 98 if (r == 0) 99 printf("from [%.*s]:%d\n", (int) sizeof(dst), dst, addr6->sin6_port); 100 if (addr6->sin6_port != client_port) 101 return 0; 102 if (r != 0 || strcmp(dst, "::ffff:127.0.0.1")) 103 return 0; 104 } 105 return 1; 106} 107 108 109static void ipv6_recv_fail(uv_udp_t* handle, 110 ssize_t nread, 111 const uv_buf_t* buf, 112 const struct sockaddr* addr, 113 unsigned flags) { 114 printf("got %d %.*s\n", (int) nread, nread > 0 ? (int) nread : 0, buf->base); 115 if (!is_from_client(addr) || (nread == 0 && addr == NULL)) 116 return; 117 ASSERT(0 && "this function should not have been called"); 118} 119 120 121static void ipv6_recv_ok(uv_udp_t* handle, 122 ssize_t nread, 123 const uv_buf_t* buf, 124 const struct sockaddr* addr, 125 unsigned flags) { 126 CHECK_HANDLE(handle); 127 128 printf("got %d %.*s\n", (int) nread, nread > 0 ? (int) nread : 0, buf->base); 129 if (!is_from_client(addr) || (nread == 0 && addr == NULL)) 130 return; 131 132 ASSERT(nread == 9); 133 ASSERT(!memcmp(buf->base, data, 9)); 134 recv_cb_called++; 135} 136 137 138static void timeout_cb(uv_timer_t* timer) { 139 uv_close((uv_handle_t*)&server, close_cb); 140 uv_close((uv_handle_t*)&client, close_cb); 141 uv_close((uv_handle_t*)&timeout, close_cb); 142} 143 144 145static void do_test(uv_udp_recv_cb recv_cb, int bind_flags) { 146 struct sockaddr_in6 addr6; 147 struct sockaddr_in addr; 148 int addr6_len; 149 int addr_len; 150 uv_buf_t buf; 151 char dst[256]; 152 int r; 153 154 ASSERT(0 == uv_ip6_addr("::0", TEST_PORT, &addr6)); 155 156 r = uv_udp_init(uv_default_loop(), &server); 157 ASSERT(r == 0); 158 159 r = uv_udp_bind(&server, (const struct sockaddr*) &addr6, bind_flags); 160 ASSERT(r == 0); 161 162 addr6_len = sizeof(addr6); 163 ASSERT(uv_udp_getsockname(&server, (struct sockaddr*) &addr6, &addr6_len) == 0); 164 ASSERT(uv_inet_ntop(addr6.sin6_family, &addr6.sin6_addr, dst, sizeof(dst)) == 0); 165 printf("on [%.*s]:%d\n", (int) sizeof(dst), dst, addr6.sin6_port); 166 167 r = uv_udp_recv_start(&server, alloc_cb, recv_cb); 168 ASSERT(r == 0); 169 170 r = uv_udp_init(uv_default_loop(), &client); 171 ASSERT(r == 0); 172 173 ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); 174 ASSERT(uv_inet_ntop(addr.sin_family, &addr.sin_addr, dst, sizeof(dst)) == 0); 175 printf("to [%.*s]:%d\n", (int) sizeof(dst), dst, addr.sin_port); 176 177 /* Create some unique data to send */ 178 ASSERT(9 == snprintf(data, sizeof(data), "PING%5u", uv_os_getpid() & 0xFFFF)); 179 buf = uv_buf_init(data, 9); 180 printf("sending %s\n", data); 181 182 r = uv_udp_send(&req_, 183 &client, 184 &buf, 185 1, 186 (const struct sockaddr*) &addr, 187 send_cb); 188 ASSERT(r == 0); 189 190 addr_len = sizeof(addr); 191 ASSERT(uv_udp_getsockname(&client, (struct sockaddr*) &addr, &addr_len) == 0); 192 ASSERT(uv_inet_ntop(addr.sin_family, &addr.sin_addr, dst, sizeof(dst)) == 0); 193 printf("from [%.*s]:%d\n", (int) sizeof(dst), dst, addr.sin_port); 194 client_port = addr.sin_port; 195 196 r = uv_timer_init(uv_default_loop(), &timeout); 197 ASSERT(r == 0); 198 199 r = uv_timer_start(&timeout, timeout_cb, 500, 0); 200 ASSERT(r == 0); 201 202 ASSERT(close_cb_called == 0); 203 ASSERT(send_cb_called == 0); 204 ASSERT(recv_cb_called == 0); 205 206 uv_run(uv_default_loop(), UV_RUN_DEFAULT); 207 208 ASSERT(close_cb_called == 3); 209 210 MAKE_VALGRIND_HAPPY(); 211} 212 213 214TEST_IMPL(udp_dual_stack) { 215#if defined(__CYGWIN__) || defined(__MSYS__) 216 /* FIXME: Does Cygwin support this? */ 217 RETURN_SKIP("FIXME: This test needs more investigation on Cygwin"); 218#endif 219 220 if (!can_ipv6()) 221 RETURN_SKIP("IPv6 not supported"); 222 223#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) 224 if (!can_ipv6_ipv4_dual()) 225 RETURN_SKIP("IPv6-IPv4 dual stack not supported"); 226#elif defined(__OpenBSD__) 227 RETURN_SKIP("IPv6-IPv4 dual stack not supported"); 228#endif 229 230 do_test(ipv6_recv_ok, 0); 231 232 printf("recv_cb_called %d\n", recv_cb_called); 233 printf("send_cb_called %d\n", send_cb_called); 234 ASSERT(recv_cb_called == 1); 235 ASSERT(send_cb_called == 1); 236 237 return 0; 238} 239 240 241TEST_IMPL(udp_ipv6_only) { 242 if (!can_ipv6()) 243 RETURN_SKIP("IPv6 not supported"); 244 245 do_test(ipv6_recv_fail, UV_UDP_IPV6ONLY); 246 247 ASSERT(recv_cb_called == 0); 248 ASSERT(send_cb_called == 1); 249 250 return 0; 251} 252