Fix resolver code when IPv6 is enabled (or at least make it capable of working).
[ircu2.10.12-pk.git] / ircd / os_generic.c
1 /*
2  * IRC - Internet Relay Chat, ircd/os_generic.c
3  * Copyright (C) 1999 Thomas Helvey
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 1, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /** @file
20  * @brief Implementation of OS-dependent operations.
21  * @version $Id$
22  */
23 #include "config.h"
24
25 #define _XOPEN_SOURCE   500 /**< make limits.h #define IOV_MAX */
26 #define __EXTENSIONS__  1   /**< make Solaris netinet/in.h know IPv6 */
27
28 #include "ircd_osdep.h"
29 #include "msgq.h"
30 #include "res.h"
31 #include "s_bsd.h"
32 #include "sys.h"
33
34 /* Include file dependency notes:
35  * FreeBSD requires struct timeval from sys/time.h before struct
36  * rusage in sys/resource.h.
37  * Solaris requires sys/time.h before struct rusage (indirectly) in
38  * netinet/in.h.
39  */
40 #include <assert.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <limits.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <sys/ioctl.h>
47 #include <sys/types.h>
48 #include <sys/time.h>
49 #include <netinet/in.h>
50 #include <sys/resource.h>
51 #include <sys/socket.h>
52 #include <sys/uio.h>
53
54 #if HAVE_SYS_PARAM_H
55 #include <sys/param.h>
56 #endif
57
58 #if HAVE_UNISTD_H
59 #include <unistd.h>
60 #endif
61
62 #ifndef IOV_MAX
63 #define IOV_MAX 16      /**< minimum required length of an iovec array */
64 #endif
65
66 #ifdef HPUX
67 #include <sys/syscall.h>
68 #define getrusage(a,b) syscall(SYS_GETRUSAGE, a, b)
69 #endif
70
71 #ifdef IPV6
72 /** Native socket address type. */
73 #define sockaddr_native sockaddr_in6
74 /** Field name inside sockaddr_native to find address family. */
75 #define sn_family sin6_family
76
77 /** Convert native socket address to IRC format.
78  * @param[in] v6 Native socket address.
79  * @param[out] irc IRC format socket address.
80  */
81 void sockaddr_to_irc(const struct sockaddr_in6 *v6, struct irc_sockaddr *irc)
82 {
83     if (v6->sin6_family == AF_INET6) {
84         memcpy(&irc->addr.in6_16[0], &v6->sin6_addr, sizeof(v6->sin6_addr));
85         irc->port = ntohs(v6->sin6_port);
86     }
87     else if (v6->sin6_family == AF_INET) {
88         const struct sockaddr_in *v4 = (const struct sockaddr_in*)v6;
89         memset(&irc->addr, 0, 5*sizeof(int16_t));
90         irc->addr.in6_16[5] = 0xffff;
91         memcpy(&irc->addr.in6_16[6], &v4->sin_addr, sizeof(v4->sin_addr));
92         irc->port = ntohs(v4->sin_port);
93     }
94     else assert(0 && "Unhandled native address family");
95 }
96
97 /** Convert IRC socket address to native format.
98  * @param[out] v6 Native socket address.
99  * @param[in] irc IRC socket address.
100  * @param[in] compat_fd If non-negative, an FD specifying address family.
101  * @return Length of address written to \a v6.
102  */
103 int sockaddr_from_irc(struct sockaddr_in6 *v6, const struct irc_sockaddr *irc, int compat_fd)
104 {
105     struct sockaddr_in6 sin6;
106     socklen_t slen;
107     int family;
108
109     slen = sizeof(sin6);
110     if ((0 <= compat_fd) && (0 == getsockname(compat_fd, (struct sockaddr*)&sin6, &slen)))
111         family = sin6.sin6_family;
112     else if (irc_in_addr_is_ipv4(&VirtualHost.addr))
113         family = AF_INET;
114     else
115         family = AF_INET6;
116
117     memset(v6, 0, sizeof(*v6));
118     if (!irc) {
119         memset(v6, 0, sizeof(v6));
120         v6->sin6_family = AF_INET6;
121         return sizeof(*v6);
122     }
123     else if ((family == AF_INET) && irc_in_addr_is_ipv4(&irc->addr)) {
124         struct sockaddr_in *v4 = (struct sockaddr_in*)v6;
125         v4->sin_family = AF_INET;
126         memcpy(&v4->sin_addr, &irc->addr.in6_16[6], sizeof(v4->sin_addr));
127         v4->sin_port = htons(irc->port);
128         return sizeof(*v4);
129     }
130     else {
131         v6->sin6_family = AF_INET6;
132         memcpy(&v6->sin6_addr, &irc->addr.in6_16[0], sizeof(v6->sin6_addr));
133         v6->sin6_port = htons(irc->port);
134         return sizeof(*v6);
135     }
136 }
137
138 #else
139 #define sockaddr_native sockaddr_in
140 #define sn_family sin_family
141
142 void sockaddr_to_irc(const struct sockaddr_in *v4, struct irc_sockaddr *irc)
143 {
144     assert(v4->sin_family == AF_INET);
145     memset(&irc->addr, 0, 6*sizeof(irc->addr.in6_16[0]));
146     memcpy(&irc->addr.in6_16[6], &v4->sin_addr, sizeof(v4->sin_addr));
147     irc->port = ntohs(v4->sin_port);
148 }
149
150 int sockaddr_from_irc(struct sockaddr_in *v4, const struct irc_sockaddr *irc, int compat_fd)
151 {
152     v4->sin_family = AF_INET;
153     if (irc) {
154         assert(!irc->addr.in6_16[0] && !irc->addr.in6_16[1] && !irc->addr.in6_16[2] && !irc->addr.in6_16[3] && !irc->addr.in6_16[4] && (!irc->addr.in6_16[5] || irc->addr.in6_16[5] == 0xffff));
155         memcpy(&v4->sin_addr, &irc->addr.in6_16[6], sizeof(v4->sin_addr));
156         v4->sin_port = htons(irc->port);
157     } else{
158         memset(&v4, 0, sizeof(v4));
159     }
160     (void)compat_fd;
161     return sizeof(*v4);
162 }
163
164 #endif
165
166 /*
167  * This is part of the STATS replies. There is no offical numeric for this
168  * since this isnt an official command, in much the same way as HASH isnt.
169  * It is also possible that some systems wont support this call or have
170  * different field names for "struct rusage".
171  * -avalon
172  */
173 /** Send resource usage information to a client.
174  * @param[in] cptr Client requesting information.
175  * @param[in] uptime Wall time in seconds since the server started.
176  * @param[in] enumerator Function to call to send a line to \a cptr.
177  * @return Zero if some usage reports could not be sent, non-zero on success.
178  */
179 int os_get_rusage(struct Client *cptr, int uptime, EnumFn enumerator)
180 {
181 #ifdef HAVE_GETRUSAGE
182   char buf[256];
183   struct rusage rus;
184   time_t secs;
185
186 #ifdef  hz
187 #  define hzz hz
188 #else
189 #  ifdef HZ
190 #    define hzz HZ
191 #  else
192   int hzz = 1;
193 #  ifdef HPUX
194   hzz = sysconf(_SC_CLK_TCK);
195 #  endif
196 #endif
197 #endif
198
199   assert(0 != enumerator);
200   if (getrusage(RUSAGE_SELF, &rus) == -1)
201     return 0;
202
203   secs = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec;
204   if (secs == 0)
205     secs = 1;
206
207   sprintf(buf, "CPU Secs %ld:%ld User %ld:%ld System %ld:%ld",
208           (long)(secs / 60), (long)(secs % 60),
209           rus.ru_utime.tv_sec / 60, rus.ru_utime.tv_sec % 60,
210           rus.ru_stime.tv_sec / 60, rus.ru_stime.tv_sec % 60);
211   (*enumerator)(cptr, buf);
212
213   sprintf(buf, "RSS %ld ShMem %ld Data %ld Stack %ld",
214           rus.ru_maxrss,
215           rus.ru_ixrss / (uptime * hzz), rus.ru_idrss / (uptime * hzz),
216           rus.ru_isrss / (uptime * hzz));
217   (*enumerator)(cptr, buf);
218
219   sprintf(buf, "Swaps %ld Reclaims %ld Faults %ld",
220           rus.ru_nswap, rus.ru_minflt, rus.ru_majflt);
221   (*enumerator)(cptr, buf);
222
223   sprintf(buf, "Block in %ld out %ld", rus.ru_inblock, rus.ru_oublock);
224   (*enumerator)(cptr, buf);
225
226   sprintf(buf, "Msg Rcv %ld Send %ld", rus.ru_msgrcv, rus.ru_msgsnd);
227   (*enumerator)(cptr, buf);
228
229   sprintf(buf, "Signals %ld Context Vol. %ld Invol %ld",
230           rus.ru_nsignals, rus.ru_nvcsw, rus.ru_nivcsw);
231   (*enumerator)(cptr, buf);
232
233 #else /* HAVE_GETRUSAGE */
234 #if HAVE_TIMES
235   char buf[256];
236   struct tms tmsbuf;
237   time_t secs, mins;
238   int hzz = 1, ticpermin;
239   int umin, smin, usec, ssec;
240
241   assert(0 != enumerator);
242 #ifdef HPUX
243   hzz = sysconf(_SC_CLK_TCK);
244 #endif
245   ticpermin = hzz * 60;
246
247   umin = tmsbuf.tms_utime / ticpermin;
248   usec = (tmsbuf.tms_utime % ticpermin) / (float)hzz;
249   smin = tmsbuf.tms_stime / ticpermin;
250   ssec = (tmsbuf.tms_stime % ticpermin) / (float)hzz;
251   secs = usec + ssec;
252   mins = (secs / 60) + umin + smin;
253   secs %= hzz;
254
255   if (times(&tmsbuf) == -1)
256     return 0;
257   secs = tmsbuf.tms_utime + tmsbuf.tms_stime;
258
259   sprintf(buf, "CPU Secs %d:%d User %d:%d System %d:%d", 
260           mins, secs, umin, usec, smin, ssec);
261   (*enumerator)(cptr, buf);
262 #endif /* HAVE_TIMES */
263 #endif /* HAVE_GETRUSAGE */
264   return 1;
265 }
266
267 /** Look up the most recent socket error for a socket file descriptor.
268  * @param[in] fd File descriptor to check.
269  * @return Error code from the socket, or 0 if the OS does not support this.
270  */
271 int os_get_sockerr(int fd)
272 {
273   int    err = 0;
274 #if defined(SO_ERROR)
275   unsigned int len = sizeof(err);
276   getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
277 #endif
278   return err;
279 }
280
281 /** Set a file descriptor to non-blocking mode.
282  * @param[in] fd %Socket file descriptor.
283  * @return Non-zero on success, or zero on failure.
284  */
285 int os_set_nonblocking(int fd)
286 {
287   int res;
288 #ifndef NBLOCK_SYSV
289   int nonb = 0;
290 #endif
291
292   /*
293    * NOTE: consult ALL your relevant manual pages *BEFORE* changing
294    * these ioctl's. There are quite a few variations on them,
295    * as can be seen by the PCS one. They are *NOT* all the same.
296    * Heed this well. - Avalon.
297    */
298 #ifdef  NBLOCK_POSIX
299   nonb |= O_NONBLOCK;
300 #endif
301 #ifdef  NBLOCK_BSD
302   nonb |= O_NDELAY;
303 #endif
304 #ifdef  NBLOCK_SYSV
305   /* This portion of code might also apply to NeXT. -LynX */
306   res = 1;
307
308   if (ioctl(fd, FIONBIO, &res) == -1)
309     return 0;
310 #else
311   if ((res = fcntl(fd, F_GETFL, 0)) == -1)
312     return 0;
313   else if (fcntl(fd, F_SETFL, res | nonb) == -1)
314     return 0;
315 #endif
316   return 1;
317 }
318
319 /** Mark a socket's address as reusable.
320  * @param[in] fd %Socket file descriptor to manipulate.
321  * @return Non-zero on success, or zero on failure.
322  */
323 int os_set_reuseaddr(int fd)
324 {
325   unsigned int opt = 1;
326   return (0 == setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
327                           (const char*) &opt, sizeof(opt)));
328 }
329
330 /** Set a socket's send and receive buffer sizes.
331  * @param[in] fd %Socket file descriptor to manipulate.
332  * @param[in] ssize New send buffer size.
333  * @param[in] rsize New receive buffer size.
334  * @return Non-zero on success, or zero on failure.
335  */
336 int os_set_sockbufs(int fd, unsigned int ssize, unsigned int rsize)
337 {
338   unsigned int sopt = ssize;
339   unsigned int ropt = rsize;
340   return (0 == setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
341                           (const char*) &ropt, sizeof(ropt)) &&
342           0 == setsockopt(fd, SOL_SOCKET, SO_SNDBUF,
343                           (const char*) &sopt, sizeof(sopt)));
344 }
345
346 /** Set a socket's "type of service" value.
347  * @param[in] fd %Socket file descriptor to manipulate.
348  * @param[in] tos New type of service value to use.
349  * @return Non-zero on success, or zero on failure.
350  */
351 int os_set_tos(int fd,int tos)
352 {
353 #if defined(IP_TOS) && defined(IPPROTO_IP)
354   unsigned int opt = tos;
355   return (0 == setsockopt(fd, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)));
356 #else
357   return 1;
358 #endif
359 }
360
361 /** Disable IP options on a socket.
362  * @param[in] fd %Socket file descriptor to manipulate.
363  * @return Non-zero on success, or zero on failure.
364  */
365 int os_disable_options(int fd)
366 {
367 #if defined(IP_OPTIONS) && defined(IPPROTO_IP)
368   return (0 == setsockopt(fd, IPPROTO_IP, IP_OPTIONS, NULL, 0));
369 #else
370   return 1;
371 #endif
372 }
373
374 /*
375  * Try and find the correct name to use with getrlimit() for setting the max.
376  * number of files allowed to be open by this process.
377  */
378 #ifdef RLIMIT_FDMAX
379 #define RLIMIT_FD_MAX   RLIMIT_FDMAX
380 #else
381 #ifdef RLIMIT_NOFILE
382 #define RLIMIT_FD_MAX RLIMIT_NOFILE
383 #else
384 #ifdef RLIMIT_OPEN_MAX
385 #define RLIMIT_FD_MAX RLIMIT_OPEN_MAX
386 #else
387 #undef RLIMIT_FD_MAX
388 #endif
389 #endif
390 #endif
391
392 /** Set file descriptor limit for the process.
393  * @param[in] max_descriptors Ideal number of file descriptors.
394  * @return Zero on success; -1 on error; positive number of possible
395  * file descriptors if \a max_descriptors is too high.
396  */
397 int os_set_fdlimit(unsigned int max_descriptors)
398 {
399 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_FD_MAX)
400   struct rlimit limit;
401
402   if (!getrlimit(RLIMIT_FD_MAX, &limit)) {
403     if (limit.rlim_max < max_descriptors)
404       return limit.rlim_max;
405     limit.rlim_cur = limit.rlim_max;    /* make soft limit the max */
406     return setrlimit(RLIMIT_FD_MAX, &limit);
407   }
408 #endif /* defined(HAVE_SETRLIMIT) && defined(RLIMIT_FD_MAX) */
409   return 0;
410 }
411
412 /** Attempt to read from a non-blocking socket.
413  * @param[in] fd File descriptor to read from.
414  * @param[out] buf Output buffer to read into.
415  * @param[in] length Number of bytes to read.
416  * @param[out] count_out Receives number of bytes actually read.
417  * @return An IOResult value indicating status.
418  */
419 IOResult os_recv_nonb(int fd, char* buf, unsigned int length,
420                  unsigned int* count_out)
421 {
422   int res;
423   assert(0 != buf);
424   assert(0 != count_out);
425   *count_out = 0;
426   errno = 0;
427
428   if (0 < (res = recv(fd, buf, length, 0))) {
429     *count_out = (unsigned) res;
430     return IO_SUCCESS;
431   }
432   else if (res < 0) {
433     if (EWOULDBLOCK == errno || EAGAIN == errno
434 #ifdef ENOMEM
435         || ENOMEM == errno
436 #endif
437 #ifdef ENOBUFS
438         || ENOBUFS == errno
439 #endif
440         )
441       return IO_BLOCKED;
442     else
443       return IO_FAILURE;
444   }
445   /*
446    * 0   == client closed the connection
447    * < 1 == error
448    */
449   return IO_FAILURE;
450 }
451
452 /** Attempt to read from a non-blocking UDP socket.
453  * @param[in] fd File descriptor to read from.
454  * @param[out] buf Output buffer to read into.
455  * @param[in] length Number of bytes to read.
456  * @param[out] length_out Receives number of bytes actually read.
457  * @param[out] addr_out Peer address that sent the message.
458  * @return An IOResult value indicating status.
459  */
460 IOResult os_recvfrom_nonb(int fd, char* buf, unsigned int length,
461                           unsigned int* length_out,
462                           struct irc_sockaddr* addr_out)
463 {
464   struct sockaddr_native addr;
465   unsigned int len = sizeof(addr);
466   int    res;
467   assert(0 != buf);
468   assert(0 != length_out);
469   assert(0 != addr_out);
470   errno = 0;
471   *length_out = 0;
472
473   res = recvfrom(fd, buf, length, 0, (struct sockaddr*) &addr, &len);
474   if (-1 == res) {
475     if (EWOULDBLOCK == errno || ENOMEM == errno
476 #ifdef ENOMEM
477         || ENOMEM == errno
478 #endif
479 #ifdef ENOBUFS
480         || ENOBUFS == errno
481 #endif
482         )
483       return IO_BLOCKED;
484     return IO_FAILURE;
485   }
486   sockaddr_to_irc(&addr, addr_out);
487   *length_out = res;
488   return IO_SUCCESS;
489 }
490
491 /** Attempt to write on a non-blocking UDP socket.
492  * @param[in] fd File descriptor to write to.
493  * @param[in] buf Output buffer to send from.
494  * @param[in] length Number of bytes to write.
495  * @param[out] count_out Receives number of bytes actually written.
496  * @param[in] flags Flags for call to sendto().
497  * @param[in] peer Destination address of the message.
498  * @return An IOResult value indicating status.
499  */
500 IOResult os_sendto_nonb(int fd, const char* buf, unsigned int length,
501                         unsigned int* count_out, unsigned int flags,
502                         const struct irc_sockaddr* peer)
503 {
504   struct sockaddr_native addr;
505   int res, size;
506   assert(0 != buf);
507   if (count_out)
508     *count_out = 0;
509   errno = 0;
510
511   size = sockaddr_from_irc(&addr, peer, fd);
512   if (-1 < (res = sendto(fd, buf, length, flags, (struct sockaddr*)&addr, size))) {
513     if (count_out)
514       *count_out = (unsigned) res;
515     return IO_SUCCESS;
516   }
517   else if (EWOULDBLOCK == errno || EAGAIN == errno
518 #ifdef ENOMEM
519            || ENOMEM == errno
520 #endif
521 #ifdef ENOBUFS
522            || ENOBUFS == errno
523 #endif
524       )
525     return IO_BLOCKED;
526   return IO_FAILURE;
527 }
528
529 /** Attempt to write on a connected socket.
530  * @param[in] fd File descriptor to write to.
531  * @param[in] buf Output buffer to send from.
532  * @param[in] length Number of bytes to write.
533  * @param[out] count_out Receives number of bytes actually written.
534  * @return An IOResult value indicating status.
535  */
536 IOResult os_send_nonb(int fd, const char* buf, unsigned int length, 
537                  unsigned int* count_out)
538 {
539   int res;
540   assert(0 != buf);
541   assert(0 != count_out);
542   *count_out = 0;
543   errno = 0;
544
545   if (-1 < (res = send(fd, buf, length, 0))) {
546     *count_out = (unsigned) res;
547     return IO_SUCCESS;
548   }
549   else if (EWOULDBLOCK == errno || EAGAIN == errno
550 #ifdef ENOMEM
551            || ENOMEM == errno
552 #endif
553 #ifdef ENOBUFS
554            || ENOBUFS == errno
555 #endif
556       )
557     return IO_BLOCKED;
558   return IO_FAILURE;
559 }
560
561 /** Attempt a vectored write on a connected socket.
562  * @param[in] fd File descriptor to write to.
563  * @param[in] buf Message queue to send from.
564  * @param[out] count_in Number of bytes mapped from \a buf.
565  * @param[out] count_out Receives number of bytes actually written.
566  * @return An IOResult value indicating status.
567  */
568 IOResult os_sendv_nonb(int fd, struct MsgQ* buf, unsigned int* count_in,
569                        unsigned int* count_out)
570 {
571   int res;
572   int count;
573   struct iovec iov[IOV_MAX];
574
575   assert(0 != buf);
576   assert(0 != count_in);
577   assert(0 != count_out);
578
579   *count_in = 0;
580   *count_out = 0;
581   errno = 0;
582
583   count = msgq_mapiov(buf, iov, IOV_MAX, count_in);
584
585   if (-1 < (res = writev(fd, iov, count))) {
586     *count_out = (unsigned) res;
587     return IO_SUCCESS;
588   }
589   else if (EWOULDBLOCK == errno || EAGAIN == errno
590 #ifdef ENOMEM
591            || ENOMEM == errno
592 #endif
593 #ifdef ENOBUFS
594            || ENOBUFS == errno
595 #endif
596       )
597     return IO_BLOCKED;
598
599   return IO_FAILURE;
600 }
601
602 /** Open a TCP or UDP socket on a particular address.
603  * @param[in] local Local address to bind to.
604  * @param[in] type SOCK_STREAM or SOCK_DGRAM.
605  * @param[in] port_name Port name (used in error diagnostics).
606  * @return Bound descriptor, or -1 on error.
607  */
608 int os_socket(const struct irc_sockaddr* local, int type, const char* port_name)
609 {
610   struct sockaddr_native addr;
611   int size, fd;
612
613   size = sockaddr_from_irc(&addr, local, -1);
614   fd = socket(addr.sn_family, type, 0);
615   if (fd < 0) {
616     report_error(SOCKET_ERROR_MSG, port_name, errno);
617     return -1;
618   }
619   if (fd > MAXCLIENTS - 1) {
620     report_error(CONNLIMIT_ERROR_MSG, port_name, 0);
621     close(fd);
622     return -1;
623   }
624   if (!os_set_reuseaddr(fd)) {
625     report_error(REUSEADDR_ERROR_MSG, port_name, errno);
626     close(fd);
627     return -1;
628   }
629   if (!os_set_nonblocking(fd)) {
630     report_error(NONB_ERROR_MSG, port_name, errno);
631     close(fd);
632     return -1;
633   }
634   if (local) {
635     if (bind(fd, (struct sockaddr*)&addr, size)) {
636       report_error(BIND_ERROR_MSG, port_name, errno);
637       close(fd);
638       return -1;
639     }
640   }
641   return fd;
642 }
643
644 /** Accept a connection on a socket.
645  * @param[in] fd Listening file descriptor.
646  * @param[out] peer Peer address of connection.
647  * @return File descriptor for accepted connection.
648  */
649 int os_accept(int fd, struct irc_sockaddr* peer)
650 {
651   struct sockaddr_native addr;
652   socklen_t addrlen;
653   int new_fd;
654
655   addrlen = sizeof(addr);
656   new_fd = accept(fd, (struct sockaddr*)&addr, &addrlen);
657   if (new_fd < 0)
658     memset(peer, 0, sizeof(*peer));
659   else
660     sockaddr_to_irc(&addr, peer);
661   return new_fd;
662 }
663
664 /** Start a non-blocking connection.
665  * @param[in] fd Disconnected file descriptor.
666  * @param[in] sin Target address for connection.
667  * @return IOResult code indicating status.
668  */
669 IOResult os_connect_nonb(int fd, const struct irc_sockaddr* sin)
670 {
671   struct sockaddr_native addr;
672   int size;
673
674   size = sockaddr_from_irc(&addr, sin, fd);
675   if (connect(fd, (struct sockaddr*) &addr, size))
676     return (errno == EINPROGRESS) ? IO_BLOCKED : IO_FAILURE;
677   return IO_SUCCESS;
678 }
679
680 /** Get local address of a socket.
681  * @param[in] fd File descriptor to operate on.
682  * @param[out] sin_out Receives local socket address.
683  * @return Non-zero on success; zero on error.
684  */
685 int os_get_sockname(int fd, struct irc_sockaddr* sin_out)
686 {
687   struct sockaddr_native addr;
688   unsigned int len = sizeof(addr);
689
690   assert(0 != sin_out);
691   if (getsockname(fd, (struct sockaddr*) &addr, &len))
692     return 0;
693   sockaddr_to_irc(&addr, sin_out);
694   return 1;
695 }
696
697 /** Get remote address of a socket.
698  * @param[in] fd File descriptor to operate on.
699  * @param[out] sin_out Receives remote socket address.
700  * @return Non-zero on success; zero on error.
701  */
702 int os_get_peername(int fd, struct irc_sockaddr* sin_out)
703 {
704   struct sockaddr_native addr;
705   unsigned int len = sizeof(addr);
706
707   assert(0 != sin_out);
708   if (getpeername(fd, (struct sockaddr*) &addr, &len))
709     return 0;
710   sockaddr_to_irc(&addr, sin_out);
711   return 1;
712 }
713
714 /** Start listening on a socket.
715  * @param[in] fd Disconnected file descriptor.
716  * @param[in] backlog Maximum number of un-accept()ed connections to keep.
717  * @return Non-zero on success; zero on error.
718  */
719 int os_set_listen(int fd, int backlog)
720 {
721   return (0 == listen(fd, backlog));
722 }