socket-util.c revision 47be870bd83fb3719dffc3ee9348a409ab762a14
/*-*- Mode: C; c-basic-offset: 8 -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
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.
systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include "macro.h"
#include "util.h"
#include "socket-util.h"
int socket_address_parse(SocketAddress *a, const char *s) {
int r;
char *e, *n;
unsigned u;
assert(a);
assert(s);
zero(*a);
a->type = SOCK_STREAM;
if (*s == '[') {
/* IPv6 in [x:.....:z]:p notation */
return -EINVAL;
return -ENOMEM;
errno = 0;
free(n);
}
free(n);
e++;
if (*e != ':')
return -EINVAL;
e++;
if ((r = safe_atou(e, &u)) < 0)
return r;
if (u <= 0 || u > 0xFFFF)
return -EINVAL;
a->size = sizeof(struct sockaddr_in6);
} else if (*s == '/') {
/* AF_UNIX socket */
size_t l;
l = strlen(s);
return -EINVAL;
} else if (*s == '@') {
/* Abstract AF_UNIX socket */
size_t l;
l = strlen(s+1);
return -EINVAL;
a->size = sizeof(struct sockaddr_un);
} else {
if ((e = strchr(s, ':'))) {
if ((r = safe_atou(e+1, &u)) < 0)
return r;
if (u <= 0 || u > 0xFFFF)
return -EINVAL;
if (!(n = strndup(s, e-s)))
return -ENOMEM;
/* IPv4 in w.x.y.z:p notation? */
free(n);
return -errno;
}
if (r > 0) {
/* Gotcha, it's a traditional IPv4 address */
free(n);
a->size = sizeof(struct sockaddr_in);
} else {
unsigned idx;
free(n);
return -EINVAL;
}
/* Uh, our last resort, an interface name */
idx = if_nametoindex(n);
free(n);
if (idx == 0)
return -EINVAL;
a->size = sizeof(struct sockaddr_in6);
}
} else {
/* Just a port */
if ((r = safe_atou(s, &u)) < 0)
return r;
if (u <= 0 || u > 0xFFFF)
return -EINVAL;
a->size = sizeof(struct sockaddr_in6);
}
}
return 0;
}
int socket_address_verify(const SocketAddress *a) {
assert(a);
switch (socket_address_family(a)) {
case AF_INET:
if (a->size != sizeof(struct sockaddr_in))
return -EINVAL;
return -EINVAL;
return 0;
case AF_INET6:
if (a->size != sizeof(struct sockaddr_in6))
return -EINVAL;
return -EINVAL;
return 0;
case AF_UNIX:
if (a->size < sizeof(sa_family_t))
return -EINVAL;
if (a->size > sizeof(sa_family_t)) {
/* abstract */
if (a->size != sizeof(struct sockaddr_un))
return -EINVAL;
} else {
char *e;
/* path */
return -EINVAL;
return -EINVAL;
}
}
return 0;
default:
return -EAFNOSUPPORT;
}
}
int socket_address_print(const SocketAddress *a, char **p) {
int r;
assert(a);
assert(p);
if ((r = socket_address_verify(a)) < 0)
return r;
switch (socket_address_family(a)) {
case AF_INET: {
char *ret;
return -ENOMEM;
return -errno;
}
*p = ret;
return 0;
}
case AF_INET6: {
char *ret;
return -ENOMEM;
ret[0] = '[';
return -errno;
}
*p = ret;
return 0;
}
case AF_UNIX: {
char *ret;
if (a->size <= sizeof(sa_family_t)) {
return -ENOMEM;
/* abstract */
/* FIXME: We assume we can print the
* socket path here and that it hasn't
* more than one NUL byte. That is
* actually an invalid assumption */
return -ENOMEM;
ret[0] = '@';
} else {
return -ENOMEM;
}
*p = ret;
return 0;
}
default:
return -EINVAL;
}
}
int socket_address_listen(const SocketAddress *a, int backlog, SocketAddressBindIPv6Only only, const char *bind_to_device, int *ret) {
assert(a);
if ((r = socket_address_verify(a)) < 0)
return r;
return -errno;
goto fail;
}
if (bind_to_device)
goto fail;
one = 1;
goto fail;
goto fail;
if (a->type == SOCK_STREAM)
goto fail;
return 0;
fail:
r = -errno;
return r;
}