Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge pull request #9 from jesse99/master

Added logging and a better getaddrinfo
  • Loading branch information...
commit acf8f388bc2fdb2bef1774594178e9ee734e82f5 2 parents 2eaa628 + d375f5f
Josh Matthews authored

Showing 3 changed files with 130 additions and 68 deletions. Show diff stats Hide diff stats

  1. +4 0 .gitignore
  2. +1 1  Makefile
  3. +125 67 socket.rs
4 .gitignore
... ... @@ -0,0 +1,4 @@
  1 +*.dSYM
  2 +*.dylib
  3 +socket
  4 +
2  Makefile
@@ -3,7 +3,7 @@ all:
3 3
4 4 test:
5 5 rustc --test socket.rc -g
6   - $(DEBUGGER) ./socket
  6 + export RUST_LOG=socket=3 && $(DEBUGGER) ./socket
7 7
8 8 clean:
9 9 rm -rf libsocket-*
192 socket.rs
@@ -4,10 +4,14 @@ import std::rand;
4 4 export sockaddr, getaddrinfo, bind_socket, socket_handle, connect, listen, accept,
5 5 send, recv, sendto, recvfrom, setsockopt, enablesockopt, disablesockopt,
6 6 htons, htonl, ntohs, ntohl, sockaddr4_in, sockaddr6_in, sockaddr_basic,
7   - sockaddr_storage;
8   -export SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SO_REUSEADDR, SO_KEEPALIVE, SO_BROADCAST,
  7 + sockaddr_storage, inet_ntop;
  8 +export SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SO_DEBUG, SO_ACCEPTCONN, SO_REUSEADDR,
  9 + SO_KEEPALIVE, SO_DONTROUTE, SO_BROADCAST, SO_LINGER, SO_OOBINLINE, SO_SNDBUF,
  10 + SO_RCVBUF, SO_SNDLOWAT, SO_RCVLOWAT, SO_SNDTIMEO, SO_RCVTIMEO, SO_ERROR, SO_TYPE,
9 11 AF_UNSPEC, AF_UNIX, AF_INET, AF_INET6, AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST,
10 12 AI_NUMERICSERV, INET6_ADDRSTRLEN;
  13 +
  14 +type c_str = *libc::c_char;
11 15
12 16 #[nolink]
13 17 native mod c {
@@ -33,10 +37,11 @@ native mod c {
33 37 fn ntohs(netshort: u16) -> u16;
34 38 fn ntohl(netlong: u32) -> u32;
35 39
36   - fn inet_ntop(af: libc::c_int, src: *libc::c_void, dst: *u8, size: socklen_t) -> *u8;
37   - fn inet_pton(af: libc::c_int, src: *u8, dst: *libc::c_void) -> libc::c_int;
  40 + fn inet_ntop(af: libc::c_int, src: *libc::c_void, dst: *u8, size: socklen_t) -> c_str;
  41 + fn inet_pton(af: libc::c_int, src: c_str, dst: *libc::c_void) -> libc::c_int;
38 42
39   - fn getaddrinfo(node: *u8, service: *u8, hints: *addrinfo, res: **addrinfo) -> libc::c_int;
  43 + fn gai_strerror(ecode: libc::c_int) -> c_str;
  44 + fn getaddrinfo(node: c_str, service: c_str, hints: *addrinfo, res: **addrinfo) -> libc::c_int;
40 45 fn freeaddrinfo(ai: *addrinfo);
41 46 }
42 47
@@ -46,9 +51,23 @@ const SOCK_RAW: libc::c_int = 3_i32;
46 51
47 52 const SOL_SOCKET: libc::c_int = 0xffff_i32;
48 53
49   -const SO_REUSEADDR: libc::c_int = 0x0004_i32;
50   -const SO_KEEPALIVE: libc::c_int = 0x0008_i32;
51   -const SO_BROADCAST: libc::c_int = 0x0020_i32;
  54 +const SO_DEBUG: libc::c_int = 0x0001_i32; // turn on debugging info recording
  55 +const SO_ACCEPTCONN: libc::c_int = 0x0002_i32; // socket has had listen()
  56 +const SO_REUSEADDR: libc::c_int = 0x0004_i32; // allow local address reuse
  57 +const SO_KEEPALIVE: libc::c_int = 0x0008_i32; // keep connections alive
  58 +const SO_DONTROUTE: libc::c_int = 0x0010_i32; // just use interface addresses
  59 +const SO_BROADCAST: libc::c_int = 0x0020_i32; // permit sending of broadcast msgs
  60 +const SO_LINGER: libc::c_int = 0x1080_i32; // linger on close if data present (in seconds)
  61 +const SO_OOBINLINE: libc::c_int = 0x0100_i32; // leave received OOB data in line
  62 +const SO_SNDBUF: libc::c_int = 0x1001_i32; // send buffer size
  63 +const SO_RCVBUF: libc::c_int = 0x1002_i32; // receive buffer size
  64 +const SO_SNDLOWAT: libc::c_int = 0x1003_i32; // send low-water mark
  65 +const SO_RCVLOWAT: libc::c_int = 0x1004_i32; // receive low-water mark
  66 +const SO_SNDTIMEO: libc::c_int = 0x1005_i32; // send timeout
  67 +const SO_RCVTIMEO: libc::c_int = 0x1006_i32; // receive timeout
  68 +const SO_ERROR: libc::c_int = 0x1007_i32; // get error status and clear
  69 +const SO_TYPE : libc::c_int = 0x1008_i32; // get socket type
  70 +// TODO: there are a bunch of Linux specific socket options that should be added
52 71
53 72 const AF_UNSPEC: libc::c_int = 0_i32;
54 73 const AF_UNIX: libc::c_int = 1_i32;
@@ -113,81 +132,125 @@ fn mk_default_addrinfo() -> addrinfo {
113 132 ai_addr: ptr::null(), ai_canonname: ptr::null(), ai_next: ptr::null()}
114 133 }
115 134
116   -fn getaddrinfo(host: str, port: u16, f: fn(addrinfo) -> bool) unsafe {
  135 +fn getaddrinfo(host: str, port: u16, f: fn(addrinfo) -> bool) -> option<str> unsafe {
117 136 let hints = {ai_family: AF_UNSPEC, ai_socktype: SOCK_STREAM
118 137 with mk_default_addrinfo()};
119 138 let servinfo: *addrinfo = ptr::null();
120 139 let s_port = #fmt["%u", port as uint];
121   - str::as_buf(host) {|host|
122   - str::as_buf(s_port) {|port|
  140 + let mut result = option::none;
  141 + str::as_c_str(host) {|host|
  142 + str::as_c_str(s_port) {|port|
123 143 let status = c::getaddrinfo(host, port, ptr::addr_of(hints),
124 144 ptr::addr_of(servinfo));
125   - if status != -1_i32 {
  145 + if status == 0i32 {
126 146 let mut p = servinfo;
127 147 while p != ptr::null() {
128   - if f(*p) {
  148 + if !f(*p) {
129 149 break;
130 150 }
131 151 p = unsafe::reinterpret_cast((*p).ai_next);
132 152 }
  153 + } else {
  154 + #warn["getaddrinfo returned %? (%s)", status, str::unsafe::from_c_str(c::gai_strerror(status))];
  155 + result = option::some("getaddrinfo failed");
133 156 }
134 157 }
135 158 }
136 159 c::freeaddrinfo(servinfo);
  160 + result
  161 +}
  162 +
  163 +fn inet_ntop(address: addrinfo) -> str unsafe {
  164 + let buffer = vec::from_elem(INET6_ADDRSTRLEN as uint, 0u8);
  165 + c::inet_ntop(address.ai_family,
  166 + if address.ai_family == AF_INET {
  167 + let addr: *sockaddr4_in = unsafe::reinterpret_cast(address.ai_addr);
  168 + unsafe::reinterpret_cast(ptr::addr_of((*addr).sin_addr))
  169 + } else {
  170 + let addr: *sockaddr6_in = unsafe::reinterpret_cast(address.ai_addr);
  171 + unsafe::reinterpret_cast(ptr::addr_of((*addr).sin6_addr))
  172 + },
  173 + vec::unsafe::to_ptr(buffer), INET6_ADDRSTRLEN);
  174 +
  175 + // In general the result will be shorter than INET6_ADDRSTRLEN. Unfortunately
  176 + // str::from_bytes doesn't handle this case very well which causes problems if
  177 + // we want to use the string with C (e.g. with perror). See #2268.
  178 + alt vec::position(buffer, {|c| c == 0u8})
  179 + {
  180 + option::some(i)
  181 + {
  182 + str::from_bytes(vec::slice(buffer, 0u, i))
  183 + }
  184 + option::none
  185 + {
  186 + str::from_bytes(buffer)
  187 + }
  188 + }
  189 +}
  190 +
  191 +// TODO: there is no portable way to get errno from rust so, for now, we'll just write them to stderr
  192 +// See #2269.
  193 +fn log_err(mesg: str)
  194 +{
  195 + str::as_c_str(mesg) {|buffer| libc::perror(buffer)};
137 196 }
138 197
139 198 resource socket_handle(sockfd: libc::c_int) {
140 199 c::close(sockfd);
141 200 }
142 201
143   -fn bind_socket(host: str, port: u16) -> result<@socket_handle, str> {
144   - let mut fd = option::none;
145   - getaddrinfo(host, port) {|ai|
  202 +fn bind_socket(host: str, port: u16) -> result<@socket_handle, str> unsafe {
  203 + let err = for getaddrinfo(host, port) {|ai|
146 204 let sockfd = c::socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol);
147 205 if sockfd != -1_i32 {
  206 + let val = 1;
  207 + let _ = c::setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, // this shouldn't be critical so we'll ignore errors from it
  208 + unsafe::reinterpret_cast(ptr::addr_of(val)),
  209 + sys::size_of::<int>() as socklen_t);
  210 +
148 211 if c::bind(sockfd, ai.ai_addr, ai.ai_addrlen) == -1_i32 {
149 212 c::close(sockfd);
150   - false
151 213 } else {
152   - fd = option::some(sockfd);
153   - true
  214 + #debug[" bound to socket %?", sockfd];
  215 + ret result::ok(@socket_handle(sockfd));
154 216 }
155 217 } else {
156   - false
  218 + log_err(#fmt["socket(%s) error", inet_ntop(ai)]);
157 219 }
158   - }
159   - if option::is_some(fd) {
160   - result::ok(@socket_handle(option::get(fd)))
161   - } else {
162   - result::err("bind failed")
  220 + };
  221 + alt err
  222 + {
  223 + option::some(mesg) {result::err(mesg)}
  224 + option::none {result::err("bind failed to find an address")}
163 225 }
164 226 }
165 227
166 228 fn connect(host: str, port: u16) -> result<@socket_handle, str> {
167   - let mut fd = option::none;
168   - getaddrinfo(host, port) {|ai|
  229 + #info["connecting to %s:%?", host, port];
  230 + let err = for getaddrinfo(host, port) {|ai|
  231 + #debug[" trying %s", inet_ntop(ai)];
169 232 let sockfd = c::socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol);
170 233 if sockfd != -1_i32 {
171 234 if c::connect(sockfd, ai.ai_addr, ai.ai_addrlen) == -1_i32 {
172 235 c::close(sockfd);
173   - false
174 236 } else {
175   - fd = option::some(sockfd);
176   - true
  237 + #info[" connected to socket %?", sockfd];
  238 + ret result::ok(@socket_handle(sockfd));
177 239 }
178 240 } else {
179   - false
  241 + log_err(#fmt["socket(%s, %?) error", host, port]);
180 242 }
181   - }
182   - if option::is_some(fd) {
183   - result::ok(@socket_handle(option::get(fd)))
184   - } else {
185   - result::err("connect failed")
  243 + };
  244 + alt err
  245 + {
  246 + option::some(mesg) {result::err(mesg)}
  247 + option::none {result::err("connect failed to find an address")}
186 248 }
187 249 }
188 250
189 251 fn listen(sock: @socket_handle, backlog: i32) -> result<@socket_handle, str> {
190 252 if c::listen(**sock, backlog) == -1_i32 {
  253 + log_err(#fmt["listen error"]);
191 254 result::err("listen failed")
192 255 } else {
193 256 result::ok(sock)
@@ -195,12 +258,15 @@ fn listen(sock: @socket_handle, backlog: i32) -> result<@socket_handle, str> {
195 258 }
196 259
197 260 fn accept(sock: @socket_handle) -> result<@socket_handle, str> {
  261 + #info["accepting with socket %?", **sock];
198 262 let addr = (0i16, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8,0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8);
199 263 let unused: socklen_t = sys::size_of::<sockaddr>() as socklen_t;
200 264 let fd = c::accept(**sock, ptr::addr_of(addr), ptr::addr_of(unused));
201 265 if fd == -1_i32 {
  266 + log_err(#fmt["accept error"]);
202 267 result::err("accept failed")
203 268 } else {
  269 + #info["accepted socket %?", fd];
204 270 result::ok(@socket_handle(fd))
205 271 }
206 272 }
@@ -209,18 +275,21 @@ fn send(sock: @socket_handle, buf: [u8]) -> result<uint, str> unsafe {
209 275 let amt = c::send(**sock, vec::unsafe::to_ptr(buf),
210 276 vec::len(buf) as libc::c_int, 0i32);
211 277 if amt == -1_i32 {
  278 + log_err(#fmt["send error"]);
212 279 result::err("send failed")
213 280 } else {
214 281 result::ok(amt as uint)
215 282 }
216 283 }
217 284
218   -fn recv(sock: @socket_handle, len: uint) -> result<[u8], str> unsafe {
  285 +fn recv(sock: @socket_handle, len: uint) -> result<([u8], uint), str> unsafe {
219 286 let buf = vec::from_elem(len, 0u8);
220   - if c::recv(**sock, vec::unsafe::to_ptr(buf), len as libc::c_int, 0i32) == -1_i32 {
  287 + let bytes = c::recv(**sock, vec::unsafe::to_ptr(buf), len as libc::c_int, 0i32);
  288 + if bytes == -1_i32 {
  289 + log_err(#fmt["recv error"]);
221 290 result::err("recv failed")
222 291 } else {
223   - result::ok(buf)
  292 + result::ok((buf, bytes as uint))
224 293 }
225 294 }
226 295
@@ -237,6 +306,7 @@ fn sendto(sock: @socket_handle, buf: [u8], to: sockaddr)
237 306 let amt = c::sendto(**sock, vec::unsafe::to_ptr(buf), vec::len(buf) as libc::c_int, 0i32,
238 307 ptr::addr_of(to_saddr), to_len as libc::c_int);
239 308 if amt == -1_i32 {
  309 + log_err(#fmt["sendto error"]);
240 310 result::err("sendto failed")
241 311 } else {
242 312 result::ok(amt as uint)
@@ -251,6 +321,7 @@ fn recvfrom(sock: @socket_handle, len: uint)
251 321 let amt = c::recvfrom(**sock, vec::unsafe::to_ptr(buf), vec::len(buf) as libc::c_int, 0i32,
252 322 ptr::addr_of(from_saddr), ptr::addr_of(unused));
253 323 if amt == -1_i32 {
  324 + log_err(#fmt["recvfrom error"]);
254 325 result::err("recvfrom failed")
255 326 } else {
256 327 let (family, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = from_saddr;
@@ -270,6 +341,7 @@ fn setsockopt(sock: @socket_handle, option: int, value: int)
270 341 unsafe::reinterpret_cast(ptr::addr_of(val)),
271 342 sys::size_of::<int>() as socklen_t);
272 343 if r == -1_i32 {
  344 + log_err(#fmt["setsockopt error"]);
273 345 result::err("setsockopt failed")
274 346 } else {
275 347 result::ok(r)
@@ -304,19 +376,14 @@ fn ntohl(netlong: u32) -> u32 {
304 376
305 377 #[test]
306 378 fn test_server_client() {
307   - let mut port = 0u32;
308   - let mut count = 0;
309   - let rng = rand::rng();
310   - while (port < 1024u32 || port > 65535u32) {
311   - port = rng.next() % 65535u32;
312   - count += 1;
313   - assert count < 100;
314   - }
  379 + #info["---- test_server_client ------------------------"];
  380 + let port = 48006u16;
315 381 let test_str = "testing";
316 382
317 383 let r = result::chain(bind_socket("localhost", port as u16)) {|s|
318 384 result::chain(listen(s, 1i32)) {|s|
319 385
  386 + // client
320 387 task::spawn {||
321 388 result::chain(connect("localhost", port as u16)) {|s|
322 389 let res = send(s, str::bytes(test_str));
@@ -325,10 +392,13 @@ fn test_server_client() {
325 392 };
326 393 };
327 394
  395 + // server
328 396 result::chain(accept(s)) {|c|
329   - let res = recv(c, str::len(test_str));
  397 + let res = recv(c, 1024u);
330 398 assert result::is_success(res);
331   - assert result::get(res) == str::bytes(test_str);
  399 + let (buffer, len) = result::get(res);
  400 + assert len == str::len(test_str);
  401 + assert vec::slice(buffer, 0u, len) == str::bytes(test_str);
332 402 result::ok(c)
333 403 }
334 404 }
@@ -338,15 +408,12 @@ fn test_server_client() {
338 408
339 409 #[test]
340 410 fn test_getaddrinfo_localhost() {
  411 + #info["---- test_getaddrinfo_localhost ------------------------"];
341 412 let hints = {ai_family: AF_UNSPEC, ai_socktype: SOCK_STREAM with mk_default_addrinfo()};
342 413 let servinfo: *addrinfo = ptr::null();
343   - let mut port = 0u32;
344   - let rng = rand::rng();
345   - while (port < 1024u32 || port > 65535u32) {
346   - port = rng.next();
347   - }
348   - str::as_buf("localhost") {|host|
349   - str::as_buf(#fmt["%u", port as uint]) {|p|
  414 + let port = 48007u16;
  415 + str::as_c_str("localhost") {|host|
  416 + str::as_c_str(#fmt["%u", port as uint]) {|p|
350 417 let status = c::getaddrinfo(host, p, ptr::addr_of(hints), ptr::addr_of(servinfo));
351 418 assert status == 0_i32;
352 419 unsafe {
@@ -354,17 +421,8 @@ fn test_getaddrinfo_localhost() {
354 421 let p = *servinfo;
355 422 assert p.ai_next != ptr::null();
356 423
357   - let ipstr = vec::from_elem(INET6_ADDRSTRLEN as uint, 0u8);
358   - c::inet_ntop(p.ai_family,
359   - if p.ai_family == AF_INET {
360   - let addr: *sockaddr4_in = unsafe::reinterpret_cast(p.ai_addr);
361   - unsafe::reinterpret_cast(ptr::addr_of((*addr).sin_addr))
362   - } else {
363   - let addr: *sockaddr6_in = unsafe::reinterpret_cast(p.ai_addr);
364   - unsafe::reinterpret_cast(ptr::addr_of((*addr).sin6_addr))
365   - },
366   - vec::unsafe::to_ptr(ipstr), INET6_ADDRSTRLEN);
367   - let val = str::split_char(str::from_bytes(ipstr), 0 as char)[0];
  424 + let ipstr = inet_ntop(p);
  425 + let val = str::split_char(ipstr, 0 as char)[0];
368 426 assert str::eq("127.0.0.1", val) || str::eq("::1", val)
369 427 }
370 428 c::freeaddrinfo(servinfo)

0 comments on commit acf8f38

Please sign in to comment.
Something went wrong with that request. Please try again.