/* mplpc.c @description@ Copyright (C) 1996-1997 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ static const char rcsid[] = "$Id$"; #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "mpdosock.h" //#include "types.h" typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned long DWORD; //#include "lpc.h" typedef struct { short version; // version of LPC requested short sizeOfArgs; // size of arguments short service; // service # requested char Data[1]; // data } LPCData; typedef struct { short version; // LPC version short sizeOfReturn; // return data size short error; // any error codes short noRet; // number of returns char Data[1]; // data } LPCReturn; //#include "services.h" #define MAXSOCKETS 20 // services #define LPC_SOCKBIND 4 #define LPC_SOCKGETHOSTBYNAME 5 #define LPC_SOCKGETHOSTNAME 6 #define LPC_SOCKGETHOSTBYADDR 7 #define LPC_SOCKCLOSE 8 #define LPC_SOCKSOCKET 9 #define LPC_SOCKRECVFROM 10 #define LPC_SOCKSENDTO 11 #define LPC_SOCKIOCTL 12 #define LPC_SOCKGETSOCKNAME 13 #define LPC_SOCKFLUSH 14 #define LPC_SOCKSETOPT 15 #define LPC_SOCKGETLASTERROR 16 #define LPC_SOCKINETADDR 17 // htons, ntohs, htonl, ntohl implemented locally // errors #define LPC_UNRECOGNIZED_SERVICE -1 #define LPC_NOERROR 0 // structures for support typedef struct { SOCKET s; int namelen; char name[1]; } BindArgs; typedef struct { SOCKET s; long cmd; char data[1]; } IoctlArgs; typedef struct { int retVal; int namelen; char name[1]; } GetSockNameRet; typedef GetSockNameRet GetHostNameRet; typedef struct { int retVal; int h_addr_0; // that's the only important value } GetHostByNameRet; typedef struct { int len; int type; char addr[1]; } GetHostByAddrArgs; typedef struct { int retVal; char h_name[1]; // h_name is the only important value } GetHostByAddrRet; typedef struct { SOCKET s; int flags; } RecvFromArgs; typedef struct { int retVal; int errCode; int len; // message len struct sockaddr sockaddr; int sockaddrlen; char Data[1]; } RecvFromRet; typedef struct { SOCKET s; int flags; int len; struct sockaddr sockaddr; int sockaddrlen; char Data[1]; } SendToArgs; typedef struct { int retVal; int errCode; } SendToRet; typedef struct { int bufflen; SOCKET s; int len; int sockaddrlen; struct sockaddr address; char data[1]; } SocketChannelData; typedef struct { int af; int type; int protocol; } SocketArgs; typedef struct { SOCKET s; int len; int flags; int addrlen; struct sockaddr addr; char data[1]; } WinSockData; typedef struct { SOCKET s; int level; int optname; int optlen; char optval[1]; } SetSockOptArgs; typedef struct { SOCKET sock[MAXSOCKETS]; } SocketMap; //#include "rtq.h" #define RTQ_NODE struct rtq_node RTQ_NODE { RTQ_NODE *self; // Ring zero address of this node RTQ_NODE *left; // Ring zero address of preceding // node RTQ_NODE *right; // Ring zero address of succeding // node BYTE *rtqDatum; // Ring 3 Datum of Buffer (start of // preface) BYTE *rtqInsert; // Ring 3 insertion position WORD rtqLen; // Length of buffer, excluding // preface WORD rtqUpCtr; // Up Counter of bytes used so far WORD rtqQCtr; // number of nodes attached WORD padding; // DWORD alignment }; #define RTQ_PARAM_MOVENODE struct rtq_param_movenode RTQ_PARAM_MOVENODE { WORD rtqFromDQ; WORD rtqToDQ; }; RTQ_NODE *rtq_fetch (RTQ_NODE *, RTQ_NODE *); // To, From //#include "mplib.h" // give up time slice void Yield (void); void MGenWakeupDll (void); // post a message to win32 side void PostWindowsMessage (void); // get # of items on qNo int MGenGetQueueCtr (int qNo); // move first node from qFrom to qTo RTQ_NODE *MGenMoveTo (int qFrom, int qTo); // get first node from q RTQ_NODE *MGenGetNode (int q); // get master node, returning size of RTQ_NODE for size verification RTQ_NODE *MGenGetMasterNode (unsigned *size); // move all nodes from qFrom to qTo RTQ_NODE *MGenFlushNodes (int qFrom, int qTo); // count number of nodes in queues designated by bitmask // lowerOrderBits == 0..31, upperOrderBits == 32-63 int MGenMCount (unsigned lowerOrderBits, unsigned upperOrderBits); // perform consistency check on chunnel address space int MGenSanityCheck (void); #include #include extern short flat_selector; #define SOCKET_MAP_QUEUE 41 #define IDLE_QUEUE 44 #define REC_QUEUE 45 #define SEND_QUEUE 46 // queue sizes #define FREEQBASE 58 #define FREEQ64 58 #define FREEQ128 59 #define FREEQ256 60 #define FREEQ512 61 #define FREEQ1024 62 #define FREEQ2048 63 #define NFREEQ 6 #define QLIMIT 10 #define PRIVATEQ 50 #define FARPKL(x) (_farnspeekl((unsigned long) x)) #define FARPKB(x) (_farnspeekb((unsigned long) x)) #define FARPKS(x) (_farnspeekw((unsigned long) x)) #define FARPOKL(x, y) (_farnspokel((unsigned long) x, (unsigned long) y)) #define FARPOKB(x, y) (_farnspokeb((unsigned long) x, (unsigned char) y)) int Qsizes[] = { 64, 128, 256, 512, 1024, 2048 }; int SocketError = 0; SocketMap *SockMap; #define HOSTENT_ALIAS_LIMIT 5 #define HOSTENT_STRLEN_LIMIT 50 #define HOSTENT_ADDR_LIST_LIMIT 5 struct hostent HostEnt; char HostEnt_hname[HOSTENT_STRLEN_LIMIT]; char *HostEnt_h_aliases[HOSTENT_ALIAS_LIMIT]; char HostEnt_names[HOSTENT_ALIAS_LIMIT][HOSTENT_STRLEN_LIMIT]; struct in_addr *HostEnt_addr_list[HOSTENT_ADDR_LIST_LIMIT]; struct in_addr HostEnt_addrs[HOSTENT_ADDR_LIST_LIMIT]; void fmemcpyto (void *to, const void *from, int length) { movedata (_my_ds (), (unsigned) from, flat_selector, (unsigned) to, length); } void fmemcpyfrom (void *to, const void *from, int length) { movedata (flat_selector, (unsigned) from, _my_ds (), (unsigned) to, length); } void fstrcpyto (char *to, const char *from) { while (*from) { FARPOKB (to, *from); to++; from++; } FARPOKB (to, 0); } void fstrncpyto (char *to, const char *from, int len) { while (*from && len) { FARPOKB (to, *from); to++; from++; len--; } FARPOKB (to, 0); } void fstrcpyfrom (char *to, const char *from) { while (FARPKB (from)) { *to = FARPKB (from); from++; to++; } *to = 0; } void fstrncpyfrom (char *to, const char *from, int len) { while (FARPKB (from) && len) { *to = FARPKB (from); from++; to++; len--; } *to = 0; } void GetSocketMap (void) { RTQ_NODE *n = MGenGetNode (SOCKET_MAP_QUEUE); SockMap = (SocketMap *) FARPKL (&n->rtqDatum); } void * GetFreeBufferToQueue (int q, int bufSize) { int i; for (i = 0; i < NFREEQ; i++) { if (Qsizes[i] >= bufSize && MGenGetQueueCtr (i + FREEQBASE)) { RTQ_NODE *n = MGenMoveTo (i + FREEQBASE, q); if (!n) continue; FARPOKL (&n->rtqUpCtr, bufSize); return (void *) FARPKL (&n->rtqDatum); } } return 0; } void FreeBufferFromQueue (int q) { int i; RTQ_NODE *n = MGenGetNode (q); for (i = 0; i < NFREEQ; i++) { if (Qsizes[i] == FARPKS (&n->rtqLen)) { MGenMoveTo (q, i + FREEQBASE); return; } } } void SetLPCData (LPCData * lpc) { FARPOKL (&(lpc->version), 1); FARPOKL (&(lpc->sizeOfArgs), 0); FARPOKL (&(lpc->service), 0); } int bind (SOCKET s, const struct sockaddr *name, int namelen) { RTQ_NODE *n = MGenGetNode (IDLE_QUEUE); LPCData *p; LPCReturn *r; BindArgs *bargs; int retVal; _farsetsel (flat_selector); SocketError = 0; p = (LPCData *) FARPKL (&n->rtqDatum); SetLPCData (p); FARPOKL (&p->service, LPC_SOCKBIND); bargs = (BindArgs *) p->Data; FARPOKL (&bargs->s, s); FARPOKL (&bargs->namelen, namelen); fmemcpyto (bargs->name, name, namelen); MGenMoveTo (IDLE_QUEUE, SEND_QUEUE); PostWindowsMessage (); while ((n = MGenGetNode (REC_QUEUE)) == 0) Yield (); r = (LPCReturn *) FARPKL (&n->rtqDatum); if (FARPKS (&r->error) != LPC_NOERROR) { return -1; } retVal = FARPKL (r->Data); // get ready for next call MGenMoveTo (REC_QUEUE, IDLE_QUEUE); return retVal; } int closesocket (SOCKET s) { RTQ_NODE *n = MGenGetNode (IDLE_QUEUE); LPCData *p; LPCReturn *r; int retVal; _farsetsel (flat_selector); SocketError = 0; p = (LPCData *) FARPKL (&n->rtqDatum); SetLPCData (p); FARPOKL (&p->service, LPC_SOCKCLOSE); FARPOKL (p->Data, s); MGenMoveTo (IDLE_QUEUE, SEND_QUEUE); PostWindowsMessage (); while ((n = MGenGetNode (REC_QUEUE)) == 0) Yield (); r = (LPCReturn *) FARPKL (&n->rtqDatum); if (FARPKS (&r->error) != LPC_NOERROR) { return -1; } retVal = FARPKL (r->Data); // get ready for next call MGenMoveTo (REC_QUEUE, IDLE_QUEUE); return retVal; } void ZapHostEnt () { // do nothing } void ReconstructHostEnt (struct hostent *s, void *flattened) { struct hostent *old = (struct hostent *) flattened; int i; char **ptr; s->h_name = HostEnt_hname; fstrncpyfrom (s->h_name, (char *) FARPKL (&old->h_name), HOSTENT_STRLEN_LIMIT - 1); s->h_name[HOSTENT_STRLEN_LIMIT - 1] = 0; s->h_aliases = HostEnt_h_aliases; ptr = (char **) FARPKL (&old->h_aliases); for (i = 0; i < (HOSTENT_ALIAS_LIMIT - 1) && FARPKL (ptr); i++, ptr++) { s->h_aliases[i] = HostEnt_names[i]; // fstrncpyfrom(s->h_aliases[i], (void *) FARPKL(ptr), // HOSTENT_STRLEN_LIMIT-1); s->h_aliases[i][HOSTENT_STRLEN_LIMIT - 1] = 0; } s->h_aliases[i] = 0; s->h_addrtype = FARPKS (&old->h_addrtype); s->h_length = FARPKS (&old->h_length); if (FARPKS (&old->h_length) != sizeof (struct in_addr)) { Con_Printf ("Error!\n"); exit (0); } s->h_addr_list = (char **) HostEnt_addr_list; ptr = (char **) FARPKL (&old->h_addr_list); for (i = 0; i < (HOSTENT_ADDR_LIST_LIMIT - 1) && FARPKL (ptr); i++, ptr++) { s->h_addr_list[i] = (char *) &(HostEnt_addrs[i]); fmemcpyfrom (s->h_addr_list[i], (void *) FARPKL (ptr), s->h_length); } s->h_addr_list[i] = 0; } int getsockname (SOCKET s, struct sockaddr *name, int *namelen) { RTQ_NODE *n = MGenGetNode (IDLE_QUEUE); LPCData *p; LPCReturn *r; GetSockNameRet *ret; int retVal; SocketError = 0; _farsetsel (flat_selector); p = (LPCData *) FARPKL (&n->rtqDatum); SetLPCData (p); FARPOKL (&p->service, LPC_SOCKGETSOCKNAME); FARPOKL (p->Data, s); MGenMoveTo (IDLE_QUEUE, SEND_QUEUE); PostWindowsMessage (); while ((n = MGenGetNode (REC_QUEUE)) == 0) Yield (); r = (LPCReturn *) FARPKL (&n->rtqDatum); if (FARPKS (&r->error) != LPC_NOERROR) { return -1; } ret = (GetSockNameRet *) r->Data; retVal = FARPKL (&ret->retVal); fmemcpyfrom (name, ret->name, FARPKL (&ret->namelen)); *namelen = FARPKL (&ret->namelen); // get ready for next call MGenMoveTo (REC_QUEUE, IDLE_QUEUE); return retVal; } int gethostname (char *name, int namelen) { RTQ_NODE *n; LPCData *p; LPCReturn *r; GetHostNameRet *ret; int retVal; char *s; _farsetsel (flat_selector); SocketError = 0; n = (RTQ_NODE *) MGenGetNode (IDLE_QUEUE); p = (LPCData *) FARPKL (&n->rtqDatum); SetLPCData (p); FARPOKL (&p->service, LPC_SOCKGETHOSTNAME); MGenMoveTo (IDLE_QUEUE, SEND_QUEUE); PostWindowsMessage (); while ((n = (RTQ_NODE *) (MGenGetNode (REC_QUEUE))) == 0) Yield (); r = (LPCReturn *) FARPKL (&n->rtqDatum); if (FARPKS (&r->error) != LPC_NOERROR) { return -1; } ret = (GetHostNameRet *) r->Data; retVal = FARPKL (&ret->retVal); s = ret->name; fstrncpyfrom (name, s, namelen); #if 0 len = strlen (ret->name); if (len > namelen) memcpy (name, ret->name, ret->namelen); else strcpy (name, ret->name); #endif // get ready for next call MGenMoveTo (REC_QUEUE, IDLE_QUEUE); return retVal; } struct hostent * gethostbyname (const char *name) { RTQ_NODE *n = MGenGetNode (IDLE_QUEUE); LPCData *p; LPCReturn *r; struct hostent *retVal; _farsetsel (flat_selector); SocketError = 0; p = (LPCData *) FARPKL (&n->rtqDatum); SetLPCData (p); FARPOKL (&p->service, LPC_SOCKGETHOSTBYNAME); fstrcpyto (p->Data, name); MGenMoveTo (IDLE_QUEUE, SEND_QUEUE); PostWindowsMessage (); while ((n = MGenGetNode (REC_QUEUE)) == 0) Yield (); r = (LPCReturn *) FARPKL (&n->rtqDatum); retVal = (struct hostent *) r->Data; if (FARPKL (&retVal->h_name) == 0) { retVal = 0; } else { ZapHostEnt (); ReconstructHostEnt (&HostEnt, (void *) retVal); retVal = &HostEnt; } // get ready for next call MGenMoveTo (REC_QUEUE, IDLE_QUEUE); return retVal; } struct hostent * gethostbyaddr (const char *addr, int len, int type) { RTQ_NODE *n = MGenGetNode (IDLE_QUEUE); LPCData *p; LPCReturn *r; GetHostByAddrArgs *args; struct hostent *retVal; SocketError = 0; _farsetsel (flat_selector); p = (LPCData *) FARPKL (&n->rtqDatum); SetLPCData (p); FARPOKL (&p->service, LPC_SOCKGETHOSTBYADDR); args = (GetHostByAddrArgs *) p->Data; FARPOKL (&args->len, len); FARPOKL (&args->type, type); fmemcpyto (args->addr, addr, len); MGenMoveTo (IDLE_QUEUE, SEND_QUEUE); PostWindowsMessage (); while ((n = MGenGetNode (REC_QUEUE)) == 0) Yield (); r = (LPCReturn *) FARPKL (&n->rtqDatum); retVal = (struct hostent *) r->Data; if (FARPKL (&retVal->h_name) == 0) { retVal = 0; } else { ZapHostEnt (); ReconstructHostEnt (&HostEnt, (void *) retVal); retVal = &HostEnt; } // get ready for next call MGenMoveTo (REC_QUEUE, IDLE_QUEUE); return retVal; } SOCKET socket (int af, int type, int protocol) { RTQ_NODE *n = MGenGetNode (IDLE_QUEUE); LPCData *p; LPCReturn *r; SocketArgs *args; int retVal; _farsetsel (flat_selector); SocketError = 0; p = (LPCData *) FARPKL (&n->rtqDatum); SetLPCData (p); FARPOKL (&p->service, LPC_SOCKSOCKET); args = (SocketArgs *) p->Data; FARPOKL (&args->af, af); FARPOKL (&args->type, type); FARPOKL (&args->protocol, protocol); MGenMoveTo (IDLE_QUEUE, SEND_QUEUE); PostWindowsMessage (); while ((n = MGenGetNode (REC_QUEUE)) == 0) Yield (); r = (LPCReturn *) FARPKL (&n->rtqDatum); if (FARPKS (&r->error) != LPC_NOERROR) { return -1; } retVal = FARPKL (r->Data); // get ready for next call MGenMoveTo (REC_QUEUE, IDLE_QUEUE); return retVal; } void sockets_flush (void) { RTQ_NODE *n = MGenGetNode (IDLE_QUEUE); LPCData *p; SocketError = 0; p = (LPCData *) FARPKL (&n->rtqDatum); SetLPCData (p); FARPOKL (&p->service, LPC_SOCKFLUSH); MGenMoveTo (IDLE_QUEUE, SEND_QUEUE); PostWindowsMessage (); while ((n = MGenGetNode (REC_QUEUE)) == 0) Yield (); MGenMoveTo (REC_QUEUE, IDLE_QUEUE); } int recvfrom (SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen) { int i; RTQ_NODE *n; WinSockData *data; int bytesRead; SocketError = 0; _farsetsel (flat_selector); if (!SockMap) GetSocketMap (); for (i = 0; i < MAXSOCKETS; i++) { if (FARPKL (&(SockMap->sock[i])) == s) break; } if (i == MAXSOCKETS) return SOCKET_ERROR; // pick up node n = MGenGetNode (i); if (n == 0) { SocketError = WSAEWOULDBLOCK; return -1; } data = (WinSockData *) FARPKL (&n->rtqDatum); bytesRead = FARPKL (&data->len); if (from) { fmemcpyfrom (from, &data->addr, sizeof (struct sockaddr)); } if (fromlen) { *fromlen = FARPKL (&data->addrlen); } fmemcpyfrom (buf, data->data, len > bytesRead ? bytesRead : len); if ((flags & MSG_PEEK) == 0) { FreeBufferFromQueue (i); } return bytesRead; } int sendto (SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen) { int i; int outQ; WinSockData *data; SocketError = 0; _farsetsel (flat_selector); if (!SockMap) GetSocketMap (); for (i = 0; i < MAXSOCKETS; i++) { if (FARPKL (&SockMap->sock[i]) == s) { break; } } if (i == MAXSOCKETS) { SocketError = WSAENOTSOCK; return SOCKET_ERROR; } outQ = i + MAXSOCKETS; if (MGenGetQueueCtr (outQ) >= QLIMIT) { SocketError = WSAEWOULDBLOCK; return SOCKET_ERROR; } data = GetFreeBufferToQueue (PRIVATEQ, len + sizeof (WinSockData)); if (!data) { SocketError = WSAEWOULDBLOCK; return SOCKET_ERROR; } FARPOKL (&data->s, s); FARPOKL (&data->len, len); if (to) { fmemcpyto (&data->addr, to, tolen); FARPOKL (&data->addrlen, tolen); } else { FARPOKL (&data->addrlen, 0); } FARPOKL (&data->flags, flags); fmemcpyto (data->data, buf, len); MGenMoveTo (PRIVATEQ, outQ); return len; } int ioctlsocket (SOCKET s, long cmd, unsigned long *argp) { RTQ_NODE *n = MGenGetNode (IDLE_QUEUE); LPCData *p; LPCReturn *r; IoctlArgs *args; int retVal; SocketError = 0; _farsetsel (flat_selector); p = (LPCData *) FARPKL (&n->rtqDatum); SetLPCData (p); FARPOKL (&p->service, LPC_SOCKIOCTL); args = (IoctlArgs *) p->Data; FARPOKL (&args->s, s); FARPOKL (&args->cmd, cmd); switch (cmd) { case FIONBIO: FARPOKL (args->data, *argp); break; default: return SOCKET_ERROR; } MGenMoveTo (IDLE_QUEUE, SEND_QUEUE); PostWindowsMessage (); while ((n = MGenGetNode (REC_QUEUE)) == 0) Yield (); r = (LPCReturn *) FARPKL (&n->rtqDatum); retVal = FARPKL (r->Data); // get ready for next call MGenMoveTo (REC_QUEUE, IDLE_QUEUE); return retVal; } int setsockopt (SOCKET s, int level, int optname, const char *optval, int optlen) { RTQ_NODE *n = MGenGetNode (IDLE_QUEUE); LPCData *p; LPCReturn *r; SetSockOptArgs *args; int retVal; SocketError = 0; _farsetsel (flat_selector); p = (LPCData *) FARPKL (&n->rtqDatum); SetLPCData (p); FARPOKL (&p->service, LPC_SOCKSETOPT); args = (SetSockOptArgs *) p->Data; FARPOKL (&args->s, s); FARPOKL (&args->level, level); FARPOKL (&args->optname, optname); FARPOKL (&args->optlen, optlen); fmemcpyto (args->optval, optval, optlen); MGenMoveTo (IDLE_QUEUE, SEND_QUEUE); PostWindowsMessage (); while ((n = MGenGetNode (REC_QUEUE)) == 0) Yield (); r = (LPCReturn *) FARPKL (&n->rtqDatum); retVal = FARPKL (r->Data); // get ready for next call MGenMoveTo (REC_QUEUE, IDLE_QUEUE); return retVal; } int WSAGetLastError (void) { RTQ_NODE *n = MGenGetNode (IDLE_QUEUE); LPCData *p; LPCReturn *r; int retVal; _farsetsel (flat_selector); if (SocketError) { int err = SocketError; SocketError = 0; return err; } p = (LPCData *) FARPKL (&n->rtqDatum); SetLPCData (p); FARPOKL (&p->service, LPC_SOCKGETLASTERROR); MGenMoveTo (IDLE_QUEUE, SEND_QUEUE); PostWindowsMessage (); while ((n = MGenGetNode (REC_QUEUE)) == 0) Yield (); r = (LPCReturn *) FARPKL (&n->rtqDatum); retVal = FARPKL (r->Data); // get ready for next call MGenMoveTo (REC_QUEUE, IDLE_QUEUE); return retVal; } unsigned long inet_addr (const char *cp) { int ret; unsigned int ha1, ha2, ha3, ha4; unsigned long ipaddr; ret = sscanf (cp, "%d.%d.%d.%d", &ha1, &ha2, &ha3, &ha4); if (ret != 4) return -1; ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4; return ipaddr; #if 0 RTQ_NODE *n = MGenGetNode (IDLE_QUEUE); LPCData *p; LPCReturn *r; int retVal; SocketError = 0; _farsetsel (flat_selector); p = (LPCData *) FARPKL (&n->rtqDatum); SetLPCData (p); FARPOKL (&p->service, LPC_SOCKINETADDR); fstrcpyto (p->Data, cp); MGenMoveTo (IDLE_QUEUE, SEND_QUEUE); PostWindowsMessage (); while ((n = MGenGetNode (REC_QUEUE)) == 0) Yield (); r = (LPCReturn *) FARPKL (&n->rtqDatum); if (FARPKS (&r->error) != LPC_NOERROR) { return -1; } retVal = FARPKL (r->Data); // get ready for next call MGenMoveTo (REC_QUEUE, IDLE_QUEUE); return retVal; #endif } char * inet_ntoa (struct in_addr in) { static char buf[32]; snprintf (buf, sizeof (buf), "%u.%u.%u.%u", in.S_un.S_un_b.s_b1, in.S_un.S_un_b.s_b2, in.S_un.S_un_b.s_b3, in.S_un.S_un_b.s_b4); return buf; }