summaryrefslogtreecommitdiffstats
path: root/lib/libwrap
diff options
context:
space:
mode:
authoritojun <itojun@openbsd.org>2002-06-07 03:32:04 +0000
committeritojun <itojun@openbsd.org>2002-06-07 03:32:04 +0000
commit67e33fe46ba81e4790a09ef4032bb26535108cbc (patch)
treeb8ac4b99c4cb69588c57c201224e268c0bcc3e11 /lib/libwrap
parentBackout previous change. Since it requires people to upgrade gcc, we want (diff)
downloadwireguard-openbsd-67e33fe46ba81e4790a09ef4032bb26535108cbc.tar.xz
wireguard-openbsd-67e33fe46ba81e4790a09ef4032bb26535108cbc.zip
support scoped IPv6 address.
no visible API change, old config files work just fine. now you can use expressions like "ALL: [fe80::%lo0/64]". theo ok
Diffstat (limited to 'lib/libwrap')
-rw-r--r--lib/libwrap/hosts_access.c220
-rw-r--r--lib/libwrap/misc.c16
-rw-r--r--lib/libwrap/socket.c177
3 files changed, 227 insertions, 186 deletions
diff --git a/lib/libwrap/hosts_access.c b/lib/libwrap/hosts_access.c
index b1d183b06f6..1043d6d811f 100644
--- a/lib/libwrap/hosts_access.c
+++ b/lib/libwrap/hosts_access.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: hosts_access.c,v 1.7 2001/12/13 17:44:47 beck Exp $ */
+/* $OpenBSD: hosts_access.c,v 1.8 2002/06/07 03:32:04 itojun Exp $ */
/*
* This module implements a simple access control language that is based on
@@ -23,7 +23,7 @@
#if 0
static char sccsid[] = "@(#) hosts_access.c 1.21 97/02/12 02:13:22";
#else
-static char rcsid[] = "$OpenBSD: hosts_access.c,v 1.7 2001/12/13 17:44:47 beck Exp $";
+static char rcsid[] = "$OpenBSD: hosts_access.c,v 1.8 2002/06/07 03:32:04 itojun Exp $";
#endif
#endif
@@ -45,12 +45,9 @@ static char rcsid[] = "$OpenBSD: hosts_access.c,v 1.7 2001/12/13 17:44:47 beck E
#ifdef NETGROUP
#include <netgroup.h>
#endif
+#include <netdb.h>
-#ifndef INADDR_NONE
-#define INADDR_NONE (-1) /* XXX should be 0xffffffff */
-#endif
-
/* Local stuff. */
#include "tcpd.h"
@@ -85,16 +82,17 @@ int resident = (-1); /* -1, 0: unknown; +1: yes */
/* Forward declarations. */
-static int table_match();
-static int list_match();
-static int server_match();
-static int client_match();
-static int host_match();
-static int string_match();
-static int masked_match();
-static int masked_match4();
+static int table_match(char *, struct request_info *);
+static int list_match(char *, struct request_info *,
+ int (*)(char *, struct request_info *));
+static int server_match(char *, struct request_info *);
+static int client_match(char *, struct request_info *);
+static int host_match(char *, struct host_info *);
+static int string_match(char *, char *);
+static int masked_match(char *, char *, char *);
+static int masked_match4(char *, char *, char *);
#ifdef INET6
-static int masked_match6();
+static int masked_match6(char *, char *, char *);
#endif
/* Size of logical line buffer. */
@@ -199,7 +197,7 @@ struct request_info *request;
static int list_match(list, request, match_fn)
char *list;
struct request_info *request;
-int (*match_fn) ();
+int (*match_fn)(char *, struct request_info *);
{
char *tok;
int l;
@@ -333,9 +331,14 @@ char *string;
#ifndef INET6
return masked_match4(net_tok, mask_tok, string);
#else
- if (dot_quad_addr_new(net_tok, NULL)
- && dot_quad_addr_new(mask_tok, NULL)
- && dot_quad_addr_new(string, NULL)) {
+ /*
+ * masked_match4() is kept just for supporting shortened IPv4 address form.
+ * If we could get rid of shortened IPv4 form, we could just always use
+ * masked_match6().
+ */
+ if (dot_quad_addr_new(net_tok, NULL) &&
+ dot_quad_addr_new(mask_tok, NULL) &&
+ dot_quad_addr_new(string, NULL)) {
return masked_match4(net_tok, mask_tok, string);
} else
return masked_match6(net_tok, mask_tok, string);
@@ -360,81 +363,152 @@ char *string;
if (!dot_quad_addr_new(string, &addr))
return (NO);
if (!dot_quad_addr_new(net_tok, &net) ||
- !dot_quad_addr_new(mask_tok, &mask)) {
+ !dot_quad_addr_new(mask_tok, &mask)) {
tcpd_warn("bad net/mask expression: %s/%s", net_tok, mask_tok);
return (NO); /* not tcpd_jump() */
}
+
+ if ((net & ~mask) != 0)
+ tcpd_warn("host bits not all zero in %s/%s", net_tok, mask_tok);
+
return ((addr & mask) == net);
}
#ifdef INET6
-/* Ugly because it covers IPv4 mapped address. I hate mapped addresses. */
static int masked_match6(net_tok, mask_tok, string)
char *net_tok;
char *mask_tok;
char *string;
{
- struct in6_addr net;
- struct in6_addr mask;
- struct in6_addr addr;
- u_long masklen;
- int fail;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } net, mask, addr;
+ struct addrinfo hints, *res;
+ unsigned long masklen;
+ char *ep;
int i;
- int maskoff;
- int netaf;
- char *p;
- const int sizoff64 = sizeof(struct in6_addr) - sizeof(struct in_addr);
-
- memset(&addr, 0, sizeof(addr));
- if (inet_pton(AF_INET6, string, &addr) == 1)
- ; /* okay */
- else if (inet_pton(AF_INET, string, &addr.s6_addr[sizoff64]) == 1)
- addr.s6_addr[10] = addr.s6_addr[11] = 0xff;
- else
- return NO;
-
- memset(&net, 0, sizeof(net));
- if (inet_pton(AF_INET6, net_tok, &net) == 1) {
- netaf = AF_INET6;
- maskoff = 0;
- } else if (inet_pton(AF_INET, net_tok, &net.s6_addr[sizoff64]) == 1) {
- netaf = AF_INET;
- maskoff = sizoff64;
- net.s6_addr[10] = net.s6_addr[11] = 0xff;
+ char *np, *mp, *ap;
+ int alen;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+ if (getaddrinfo(net_tok, "0", &hints, &res) == 0) {
+ if (res->ai_addrlen > sizeof(net) || res->ai_next) {
+ freeaddrinfo(res);
+ return NO;
+ }
+ memcpy(&net, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
} else
return NO;
- fail = 0;
- if (mask_tok[strspn(mask_tok, "0123456789")] == '\0') {
- errno = 0;
- masklen = strtoul(mask_tok, &p, 10);
- if (!*mask_tok || *p || (errno == ERANGE && masklen == ULONG_MAX))
- goto bogusmask;
- masklen += maskoff * 8;
- if (0 <= masklen && masklen <= 128) {
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = net.sa.sa_family;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+ ep = NULL;
+ if (getaddrinfo(mask_tok, "0", &hints, &res) == 0) {
+ if (res->ai_family == AF_INET6 &&
+ ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id) {
+ freeaddrinfo(res);
+ return NO;
+ }
+ if (res->ai_addrlen > sizeof(mask) || res->ai_next) {
+ freeaddrinfo(res);
+ return NO;
+ }
+ memcpy(&mask, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ } else {
+ ep = NULL;
+ masklen = strtoul(mask_tok, &ep, 10);
+ if (ep && !*ep) {
memset(&mask, 0, sizeof(mask));
- memset(&mask, 0xff, masklen / 8);
- if (masklen % 8) {
- ((u_char *)&mask)[masklen / 8] =
- (0xff00 >> (masklen % 8)) & 0xff;
+ mask.sa.sa_family = net.sa.sa_family;
+ mask.sa.sa_len = net.sa.sa_len;
+ switch (mask.sa.sa_family) {
+ case AF_INET:
+ mp = (char *)&mask.sin.sin_addr;
+ alen = sizeof(mask.sin.sin_addr);
+ break;
+ case AF_INET6:
+ mp = (char *)&mask.sin6.sin6_addr;
+ alen = sizeof(mask.sin6.sin6_addr);
+ break;
+ default:
+ return NO;
}
+ if (masklen / 8 > alen)
+ return NO;
+ memset(mp, 0xff, masklen / 8);
+ if (masklen % 8)
+ mp[masklen / 8] = 0xff00 >> (masklen % 8);
+ } else
+ return NO;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+ if (getaddrinfo(string, "0", &hints, &res) == 0) {
+ if (res->ai_addrlen > sizeof(addr) || res->ai_next) {
+ freeaddrinfo(res);
+ return NO;
+ }
+ /* special case - IPv4 mapped address */
+ if (net.sa.sa_family == AF_INET && res->ai_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr)) {
+ memset(&addr, 0, sizeof(addr));
+ addr.sa.sa_family = net.sa.sa_family;
+ addr.sa.sa_len = net.sa.sa_len;
+ memcpy(&addr.sin.sin_addr,
+ &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr.s6_addr[12],
+ sizeof(addr.sin.sin_addr));
} else
- fail++;
- } else if (netaf == AF_INET6 && inet_pton(AF_INET6, mask_tok, &mask) == 1)
- ; /* okay */
- else if (netaf == AF_INET
- && inet_pton(AF_INET, mask_tok, &mask.s6_addr[12]) == 1) {
- memset(&mask, 0xff, sizoff64);
+ memcpy(&addr, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
} else
-bogusmask:
- fail++;
- if (fail) {
- tcpd_warn("bad net/mask expression: %s/%s", net_tok, mask_tok);
- return (NO); /* not tcpd_jump() */
+ return NO;
+
+ if (net.sa.sa_family != mask.sa.sa_family ||
+ net.sa.sa_family != addr.sa.sa_family) {
+ return NO;
}
+
+ switch (net.sa.sa_family) {
+ case AF_INET:
+ np = (char *)&net.sin.sin_addr;
+ mp = (char *)&mask.sin.sin_addr;
+ ap = (char *)&addr.sin.sin_addr;
+ alen = sizeof(net.sin.sin_addr);
+ break;
+ case AF_INET6:
+ np = (char *)&net.sin6.sin6_addr;
+ mp = (char *)&mask.sin6.sin6_addr;
+ ap = (char *)&addr.sin6.sin6_addr;
+ alen = sizeof(net.sin6.sin6_addr);
+ break;
+ default:
+ return NO;
+ }
+
+ for (i = 0; i < alen; i++)
+ if (np[i] & ~mp[i]) {
+ tcpd_warn("host bits not all zero in %s/%s", net_tok, mask_tok);
+ break;
+ }
+
+ for (i = 0; i < alen; i++)
+ ap[i] &= mp[i];
- for (i = 0; i < sizeof(addr); i++)
- addr.s6_addr[i] &= mask.s6_addr[i];
- return (memcmp(&addr, &net, sizeof(addr)) == 0);
+ if (addr.sa.sa_family == AF_INET6 && addr.sin6.sin6_scope_id &&
+ addr.sin6.sin6_scope_id != net.sin6.sin6_scope_id)
+ return NO;
+ return (memcmp(ap, np, alen) == 0);
}
#endif
diff --git a/lib/libwrap/misc.c b/lib/libwrap/misc.c
index 3ba0caaf023..8c5294cc795 100644
--- a/lib/libwrap/misc.c
+++ b/lib/libwrap/misc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.c,v 1.4 2000/10/14 00:56:15 itojun Exp $ */
+/* $OpenBSD: misc.c,v 1.5 2002/06/07 03:32:04 itojun Exp $ */
/*
* Misc routines that are used by tcpd and by tcpdchk.
@@ -10,7 +10,7 @@
#if 0
static char sccsic[] = "@(#) misc.c 1.2 96/02/11 17:01:29";
#else
-static char rcsid[] = "$OpenBSD: misc.c,v 1.4 2000/10/14 00:56:15 itojun Exp $";
+static char rcsid[] = "$OpenBSD: misc.c,v 1.5 2002/06/07 03:32:04 itojun Exp $";
#endif
#endif
@@ -23,10 +23,6 @@ static char rcsid[] = "$OpenBSD: misc.c,v 1.4 2000/10/14 00:56:15 itojun Exp $";
#include "tcpd.h"
-#ifndef INADDR_NONE
-#define INADDR_NONE (-1) /* XXX should be 0xffffffff */
-#endif
-
/* xgets - fgets() with backslash-newline stripping */
char *xgets(ptr, len, fp)
@@ -60,13 +56,6 @@ char *split_at(string, delimiter)
char *string;
int delimiter;
{
-#if 0
- char *cp;
-
- if ((cp = strchr(string, delimiter)) != 0)
- *cp++ = 0;
- return (cp);
-#else
char *cp;
int bracket;
@@ -88,7 +77,6 @@ int delimiter;
}
}
return NULL;
-#endif
}
/* dot_quad_addr_new - convert dotted quad to internal form */
diff --git a/lib/libwrap/socket.c b/lib/libwrap/socket.c
index 6e5532f8401..4ca3461c466 100644
--- a/lib/libwrap/socket.c
+++ b/lib/libwrap/socket.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: socket.c,v 1.4 2001/11/07 18:49:21 deraadt Exp $ */
+/* $OpenBSD: socket.c,v 1.5 2002/06/07 03:32:04 itojun Exp $ */
/*
* This module determines the type of socket (datagram, stream), the client
@@ -21,7 +21,7 @@
#if 0
static char sccsid[] = "@(#) socket.c 1.15 97/03/21 19:27:24";
#else
-static char rcsid[] = "$OpenBSD: socket.c,v 1.4 2001/11/07 18:49:21 deraadt Exp $";
+static char rcsid[] = "$OpenBSD: socket.c,v 1.5 2002/06/07 03:32:04 itojun Exp $";
#endif
#endif
@@ -43,35 +43,35 @@ static char rcsid[] = "$OpenBSD: socket.c,v 1.4 2001/11/07 18:49:21 deraadt Exp
/* Forward declarations. */
-static void sock_sink();
-
#ifdef APPEND_DOT
+static const char *append_dot(const char *);
+#endif
+static void sock_sink(int);
+#ifdef APPEND_DOT
/*
* Speed up DNS lookups by terminating the host name with a dot. Should be
* done with care. The speedup can give problems with lookups from sources
* that lack DNS-style trailing dot magic, such as local files or NIS maps.
*/
-static struct hostent *gethostbyname_dot(name)
-char *name;
+static const char *
+append_dot(name)
+const char *name;
{
- char dot_name[MAXHOSTNAMELEN + 1];
+ static char hbuf[MAXHOSTNAMELEN + 1];
/*
* Don't append dots to unqualified names. Such names are likely to come
* from local hosts files or from NIS.
*/
- if (strchr(name, '.') == 0 || strlen(name) >= MAXHOSTNAMELEN - 1) {
- return (gethostbyname(name));
- } else {
- snprintf(dot_name, sizeof(dot_name), "%s.", name);
- return (gethostbyname(dot_name));
- }
+ if (strchr(name, '.') == 0 || strlen(name) + 2 > sizeof(hbuf))
+ strlcpy(hbuf, name, sizeof(hbuf));
+ else
+ snprintf(hbuf, sizeof(hbuf), "%s.", name);
+ return hbuf;
}
-
-#define gethostbyname gethostbyname_dot
#endif
/* sock_host - look up endpoint addresses and install conversion methods */
@@ -133,27 +133,12 @@ void sock_hostaddr(host)
struct host_info *host;
{
struct sockaddr *sa = host->sin;
- int alen, af;
- char *ap;
if (!sa)
return;
- switch (af = sa->sa_family) {
- case AF_INET:
- ap = (char *)&((struct sockaddr_in *)sa)->sin_addr;
- alen = sizeof(struct in_addr);
- break;
-#ifdef INET6
- case AF_INET6:
- ap = (char *)&((struct sockaddr_in6 *)sa)->sin6_addr;
- alen = sizeof(struct in6_addr);
- break;
-#endif
- default:
- return;
- }
host->addr[0] = '\0';
- inet_ntop(af, ap, host->addr, sizeof(host->addr));
+ getnameinfo(sa, sa->sa_len, host->addr, sizeof(host->addr),
+ NULL, 0, NI_NUMERICHOST);
}
/* sock_hostname - map endpoint address to host name */
@@ -161,95 +146,89 @@ struct host_info *host;
void sock_hostname(host)
struct host_info *host;
{
- struct sockaddr *sin = host->sin;
- struct hostent *hp;
- int i;
- int af, alen;
- char *ap;
- char hbuf[MAXHOSTNAMELEN];
+ struct sockaddr *sa = host->sin;
+ char h1[NI_MAXHOST], h2[NI_MAXHOST];
+ struct addrinfo hints, *res, *res0;
+#ifdef INET6
+ struct sockaddr_in tmp;
+#endif
- /*
- * On some systems, for example Solaris 2.3, gethostbyaddr(0.0.0.0) does
- * not fail. Instead it returns "INADDR_ANY". Unfortunately, this does
- * not work the other way around: gethostbyname("INADDR_ANY") fails. We
- * have to special-case 0.0.0.0, in order to avoid false alerts from the
- * host name/address checking code below.
- */
- if (!sin)
+ if (!sa)
return;
- switch (af = sin->sa_family) {
- case AF_INET:
- if (((struct sockaddr_in *)sin)->sin_addr.s_addr == 0)
- return;
- ap = (char *)&((struct sockaddr_in *)sin)->sin_addr;
- alen = sizeof(struct in_addr);
- break;
#ifdef INET6
- case AF_INET6:
- ap = (char *)&((struct sockaddr_in6 *)sin)->sin6_addr;
- alen = sizeof(struct in6_addr);
- /* special case on reverse lookup: mapped addr. I hate it */
- if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) {
- af = AF_INET;
- ap += (sizeof(struct in6_addr) - sizeof(struct in_addr));
- alen = sizeof(struct in_addr);
- }
- break;
+ /* special case on reverse lookup: mapped addr. I hate it */
+ if (sa->sa_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sa)->sin6_addr)) {
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.sin_family = AF_INET;
+ tmp.sin_len = sizeof(struct sockaddr_in);
+ memcpy(&tmp.sin_addr,
+ &((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[12], 4);
+ sa = (struct sockaddr *)&tmp;
+ }
#endif
- default:
+ if (getnameinfo(sa, sa->sa_len, h1, sizeof(h1), NULL, 0,
+ NI_NUMERICHOST) != 0) {
return;
}
- if ((hp = gethostbyaddr(ap, alen, af)) != 0) {
-
- strlcpy(host->name, hp->h_name, sizeof(host->name));
-
+ if (getnameinfo(sa, sa->sa_len, host->name, sizeof(host->name), NULL, 0,
+ NI_NAMEREQD) == 0) {
/*
* Verify that the address is a member of the address list returned
- * by gethostbyname(hostname).
+ * by getaddrinfo(hostname).
*
- * Verify also that gethostbyaddr() and gethostbyname() return the same
+ * Verify also that getnameinfo() and getaddrinfo() return the same
* hostname, or rshd and rlogind may still end up being spoofed.
*
- * On some sites, gethostbyname("localhost") returns "localhost.domain".
+ * On some sites, getaddrinfo("localhost") returns "localhost.domain".
* This is a DNS artefact. We treat it as a special case. When we
- * can't believe the address list from gethostbyname("localhost")
+ * can't believe the address list from getaddrinfo("localhost")
* we're in big trouble anyway.
*/
-
- if ((hp = gethostbyname2(host->name, af)) == 0) {
-
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = sa->sa_family;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_CANONNAME;
+#ifdef APPEND_DOT
+ if (getaddrinfo(append_dot(host->name), "0", &hints, &res0) != 0)
+#else
+ if (getaddrinfo(host->name, "0", &hints, &res0) != 0)
+#endif
+ {
/*
* Unable to verify that the host name matches the address. This
* may be a transient problem or a botched name server setup.
*/
- tcpd_warn("can't verify hostname: gethostbyname2(%s, %d) failed",
- host->name, af);
-
- } else if (STR_NE(host->name, hp->h_name)
- && STR_NE(host->name, "localhost")) {
-
+ tcpd_warn("can't verify hostname: getaddrinfo(%s, %d) failed",
+ host->name, hints.ai_family);
+ } else if (res0->ai_canonname &&
+ STR_NE(host->name, res0->ai_canonname) &&
+ STR_NE(host->name, "localhost")) {
/*
- * The gethostbyaddr() and gethostbyname() calls did not return
+ * The getnameinfo() and getaddrinfo() calls did not return
* the same hostname. This could be a nameserver configuration
* problem. It could also be that someone is trying to spoof us.
*/
- tcpd_warn("host name/name mismatch: %s != %.*s",
- host->name, STRING_LENGTH, hp->h_name);
-
+ tcpd_warn("host name/name mismatch: %s != %s",
+ host->name, res0->ai_canonname);
+ freeaddrinfo(res0);
} else {
-
/*
* The address should be a member of the address list returned by
- * gethostbyname(). We should first verify that the h_addrtype
- * field is AF_INET, but this program has already caused too much
- * grief on systems with broken library code.
+ * getaddrinfo().
*/
- for (i = 0; hp->h_addr_list[i]; i++) {
- if (memcmp(hp->h_addr_list[i], (char *) ap, alen) == 0)
- return; /* name is good, keep it */
+ for (res = res0; res; res = res->ai_next) {
+ if (getnameinfo(res->ai_addr, res->ai_addrlen, h2, sizeof(h2),
+ NULL, 0, NI_NUMERICHOST) != 0) {
+ continue;
+ }
+ if (STR_EQ(h1, h2)) {
+ freeaddrinfo(res0);
+ return;
+ }
}
/*
@@ -258,11 +237,11 @@ struct host_info *host;
* server.
*/
- tcpd_warn("host name/address mismatch: %s != %.*s",
- inet_ntop(af, ap, hbuf, sizeof(hbuf)),
- STRING_LENGTH, hp->h_name);
- }
+ tcpd_warn("host name/address mismatch: %s != %s", h1,
+ res0->ai_canonname ? res0->ai_canonname : "?");
+ freeaddrinfo(res0);
+ }
/* name is bad, clobber it */
strlcpy(host->name, paranoid, sizeof(host->name));
}
@@ -274,13 +253,13 @@ static void sock_sink(fd)
int fd;
{
char buf[BUFSIZ];
- struct sockaddr_storage sin;
- int size = sizeof(sin);
+ struct sockaddr_storage ss;
+ int size = sizeof(ss);
/*
* Eat up the not-yet received datagram. Some systems insist on a
* non-zero source address argument in the recvfrom() call below.
*/
- (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & sin, &size);
+ (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & ss, &size);
}