socket-util.c revision eff05270986a13e7de93ae16311f654d3f7c166f
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/***
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering This file is part of systemd.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Copyright 2010 Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is free software; you can redistribute it and/or modify it
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering under the terms of the GNU Lesser General Public License as published by
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering (at your option) any later version.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is distributed in the hope that it will be useful, but
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Lesser General Public License for more details.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering You should have received a copy of the GNU Lesser General Public License
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering***/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
b5efdb8af40ea759a1ea584c1bc44ecc81dd00ceLennart Poettering#include <assert.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <string.h>
b5efdb8af40ea759a1ea584c1bc44ecc81dd00ceLennart Poettering#include <unistd.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <errno.h>
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen#include <stdlib.h>
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen#include <arpa/inet.h>
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen#include <stdio.h>
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen#include <net/if.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <sys/types.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <sys/stat.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <stddef.h>
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering#include <sys/ioctl.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering#include "macro.h"
3c0cf502796be355431d4a64d738e75f543aa51dLennart Poettering#include "util.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "mkdir.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "path-util.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "socket-util.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "missing.h"
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering#include "fileio.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringint socket_address_parse(SocketAddress *a, const char *s) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering char *e, *n;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering unsigned u;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering int r;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen assert(a);
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering assert(s);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering zero(*a);
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen a->type = SOCK_STREAM;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering if (*s == '[') {
6073b6f26ab9fc6bf335faa7073ec443eef093fdTom Gundersen /* IPv6 in [x:.....:z]:p notation */
6073b6f26ab9fc6bf335faa7073ec443eef093fdTom Gundersen
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (!socket_ipv6_is_supported()) {
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return -EAFNOSUPPORT;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering }
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering e = strchr(s+1, ']');
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering if (!e)
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering return -EINVAL;
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering n = strndupa(s+1, e-s-1);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering errno = 0;
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering return errno > 0 ? -errno : -EINVAL;
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering e++;
3e684349c2cead2e6fd2f816c34eb17daba23a49Lennart Poettering if (*e != ':')
3e684349c2cead2e6fd2f816c34eb17daba23a49Lennart Poettering return -EINVAL;
3e684349c2cead2e6fd2f816c34eb17daba23a49Lennart Poettering
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering e++;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering r = safe_atou(e, &u);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (r < 0)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return r;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (u <= 0 || u > 0xFFFF)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return -EINVAL;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering a->sockaddr.in6.sin6_family = AF_INET6;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering a->sockaddr.in6.sin6_port = htons((uint16_t) u);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering a->size = sizeof(struct sockaddr_in6);
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
cab5b05903096e1c9cf5575ccc73f89d15c8db69Lennart Poettering } else if (*s == '/') {
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen /* AF_UNIX socket */
cab5b05903096e1c9cf5575ccc73f89d15c8db69Lennart Poettering
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen size_t l;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen l = strlen(s);
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen if (l >= sizeof(a->sockaddr.un.sun_path))
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen return -EINVAL;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
3e684349c2cead2e6fd2f816c34eb17daba23a49Lennart Poettering a->sockaddr.un.sun_family = AF_UNIX;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen memcpy(a->sockaddr.un.sun_path, s, l);
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen } else if (*s == '@') {
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen /* Abstract AF_UNIX socket */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering size_t l;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering l = strlen(s+1);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return -EINVAL;
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen a->sockaddr.un.sun_family = AF_UNIX;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen memcpy(a->sockaddr.un.sun_path+1, s+1, l);
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen } else {
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen e = strchr(s, ':');
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen if (e) {
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen r = safe_atou(e+1, &u);
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen if (r < 0)
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen return r;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen if (u <= 0 || u > 0xFFFF)
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen return -EINVAL;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen n = strndupa(s, e-s);
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen /* IPv4 in w.x.y.z:p notation? */
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering if (r < 0)
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering return -errno;
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering if (r > 0) {
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen /* Gotcha, it's a traditional IPv4 address */
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen a->sockaddr.in.sin_family = AF_INET;
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen a->sockaddr.in.sin_port = htons((uint16_t) u);
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen a->size = sizeof(struct sockaddr_in);
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen } else {
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering unsigned idx;
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering if (strlen(n) > IF_NAMESIZE-1)
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering return -EINVAL;
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen /* Uh, our last resort, an interface name */
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen idx = if_nametoindex(n);
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering if (idx == 0)
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering return -EINVAL;
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering if (!socket_ipv6_is_supported()) {
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen log_warning("Binding to interface is not available since kernel does not support IPv6.");
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen return -EAFNOSUPPORT;
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering }
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt a->sockaddr.in6.sin6_family = AF_INET6;
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering a->sockaddr.in6.sin6_port = htons((uint16_t) u);
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering a->sockaddr.in6.sin6_scope_id = idx;
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering a->sockaddr.in6.sin6_addr = in6addr_any;
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering a->size = sizeof(struct sockaddr_in6);
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering }
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering } else {
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering /* Just a port */
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering r = safe_atou(s, &u);
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt if (r < 0)
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt return r;
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt if (u <= 0 || u > 0xFFFF)
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt return -EINVAL;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering if (socket_ipv6_is_supported()) {
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering a->sockaddr.in6.sin6_family = AF_INET6;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering a->sockaddr.in6.sin6_port = htons((uint16_t) u);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering a->sockaddr.in6.sin6_addr = in6addr_any;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering a->size = sizeof(struct sockaddr_in6);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering } else {
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering a->sockaddr.in.sin_family = AF_INET;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering a->sockaddr.in.sin_port = htons((uint16_t) u);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering a->size = sizeof(struct sockaddr_in);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering }
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering }
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering }
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering return 0;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering}
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poetteringint socket_address_parse_netlink(SocketAddress *a, const char *s) {
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering int family;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering unsigned group = 0;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering _cleanup_free_ char *sfamily = NULL;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering assert(a);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering assert(s);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering zero(*a);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering a->type = SOCK_RAW;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering errno = 0;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering return errno > 0 ? -errno : -EINVAL;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering family = netlink_family_from_string(sfamily);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering if (family < 0)
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering return -EINVAL;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering a->sockaddr.nl.nl_family = AF_NETLINK;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering a->sockaddr.nl.nl_groups = group;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering a->type = SOCK_RAW;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering a->size = sizeof(struct sockaddr_nl);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering a->protocol = family;
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;
if (a->sockaddr.in.sin_port == 0)
return -EINVAL;
if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
return -EINVAL;
return 0;
case AF_INET6:
if (a->size != sizeof(struct sockaddr_in6))
return -EINVAL;
if (a->sockaddr.in6.sin6_port == 0)
return -EINVAL;
if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
return -EINVAL;
return 0;
case AF_UNIX:
if (a->size < offsetof(struct sockaddr_un, sun_path))
return -EINVAL;
if (a->size > offsetof(struct sockaddr_un, sun_path)) {
if (a->sockaddr.un.sun_path[0] != 0) {
char *e;
/* path */
e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
if (!e)
return -EINVAL;
if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
return -EINVAL;
}
}
if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
return -EINVAL;
return 0;
case AF_NETLINK:
if (a->size != sizeof(struct sockaddr_nl))
return -EINVAL;
if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
return -EINVAL;
return 0;
default:
return -EAFNOSUPPORT;
}
}
int socket_address_print(const SocketAddress *a, char **ret) {
int r;
assert(a);
assert(ret);
r = socket_address_verify(a);
if (r < 0)
return r;
if (socket_address_family(a) == AF_NETLINK) {
_cleanup_free_ char *sfamily = NULL;
r = netlink_family_to_string_alloc(a->protocol, &sfamily);
if (r < 0)
return r;
r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
if (r < 0)
return -ENOMEM;
return 0;
}
return sockaddr_pretty(&a->sockaddr.sa, a->size, false, ret);
}
bool socket_address_can_accept(const SocketAddress *a) {
assert(a);
return
a->type == SOCK_STREAM ||
a->type == SOCK_SEQPACKET;
}
bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
assert(a);
assert(b);
/* Invalid addresses are unequal to all */
if (socket_address_verify(a) < 0 ||
socket_address_verify(b) < 0)
return false;
if (a->type != b->type)
return false;
if (a->size != b->size)
return false;
if (socket_address_family(a) != socket_address_family(b))
return false;
switch (socket_address_family(a)) {
case AF_INET:
if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr)
return false;
if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port)
return false;
break;
case AF_INET6:
if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
return false;
if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
return false;
break;
case AF_UNIX:
if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
return false;
if (a->sockaddr.un.sun_path[0]) {
if (!strneq(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)))
return false;
} else {
if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
return false;
}
break;
case AF_NETLINK:
if (a->protocol != b->protocol)
return false;
if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
return false;
break;
default:
/* Cannot compare, so we assume the addresses are different */
return false;
}
return true;
}
bool socket_address_is(const SocketAddress *a, const char *s, int type) {
struct SocketAddress b;
assert(a);
assert(s);
if (socket_address_parse(&b, s) < 0)
return false;
b.type = type;
return socket_address_equal(a, &b);
}
bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
struct SocketAddress b;
assert(a);
assert(s);
if (socket_address_parse_netlink(&b, s) < 0)
return false;
return socket_address_equal(a, &b);
}
const char* socket_address_get_path(const SocketAddress *a) {
assert(a);
if (socket_address_family(a) != AF_UNIX)
return NULL;
if (a->sockaddr.un.sun_path[0] == 0)
return NULL;
return a->sockaddr.un.sun_path;
}
bool socket_ipv6_is_supported(void) {
char *l = 0;
bool enabled;
if (access("/sys/module/ipv6", F_OK) != 0)
return 0;
/* If we can't check "disable" parameter, assume enabled */
if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0)
return 1;
/* If module was loaded with disable=1 no IPv6 available */
enabled = l[0] == '0';
free(l);
return enabled;
}
bool socket_address_matches_fd(const SocketAddress *a, int fd) {
union sockaddr_union sa;
socklen_t salen = sizeof(sa), solen;
int protocol, type;
assert(a);
assert(fd >= 0);
if (getsockname(fd, &sa.sa, &salen) < 0)
return false;
if (sa.sa.sa_family != a->sockaddr.sa.sa_family)
return false;
solen = sizeof(type);
if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &solen) < 0)
return false;
if (type != a->type)
return false;
if (a->protocol != 0) {
solen = sizeof(protocol);
if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &solen) < 0)
return false;
if (protocol != a->protocol)
return false;
}
switch (sa.sa.sa_family) {
case AF_INET:
return sa.in.sin_port == a->sockaddr.in.sin_port &&
sa.in.sin_addr.s_addr == a->sockaddr.in.sin_addr.s_addr;
case AF_INET6:
return sa.in6.sin6_port == a->sockaddr.in6.sin6_port &&
memcmp(&sa.in6.sin6_addr, &a->sockaddr.in6.sin6_addr, sizeof(struct in6_addr)) == 0;
case AF_UNIX:
return salen == a->size &&
memcmp(sa.un.sun_path, a->sockaddr.un.sun_path, salen - offsetof(struct sockaddr_un, sun_path)) == 0;
}
return false;
}
int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, char **ret) {
union sockaddr_union *sa = (union sockaddr_union*) _sa;
char *p;
assert(sa);
assert(salen >= sizeof(sa->sa.sa_family));
switch (sa->sa.sa_family) {
case AF_INET: {
uint32_t a;
a = ntohl(sa->in.sin_addr.s_addr);
if (asprintf(&p,
"%u.%u.%u.%u:%u",
a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
ntohs(sa->in.sin_port)) < 0)
return -ENOMEM;
break;
}
case AF_INET6: {
static const unsigned char ipv4_prefix[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
};
if (translate_ipv6 && memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
const uint8_t *a = sa->in6.sin6_addr.s6_addr+12;
if (asprintf(&p,
"%u.%u.%u.%u:%u",
a[0], a[1], a[2], a[3],
ntohs(sa->in6.sin6_port)) < 0)
return -ENOMEM;
} else {
char a[INET6_ADDRSTRLEN];
if (asprintf(&p,
"[%s]:%u",
inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a)),
ntohs(sa->in6.sin6_port)) < 0)
return -ENOMEM;
}
break;
}
case AF_UNIX:
if (salen <= offsetof(struct sockaddr_un, sun_path)) {
p = strdup("<unnamed>");
if (!p)
return -ENOMEM;
} else if (sa->un.sun_path[0] == 0) {
/* 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 */
p = new(char, sizeof(sa->un.sun_path)+1);
if (!p)
return -ENOMEM;
p[0] = '@';
memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1);
p[sizeof(sa->un.sun_path)] = 0;
} else {
p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path));
if (!ret)
return -ENOMEM;
}
break;
default:
return -ENOTSUP;
}
*ret = p;
return 0;
}
int getpeername_pretty(int fd, char **ret) {
union sockaddr_union sa;
socklen_t salen;
int r;
assert(fd >= 0);
assert(ret);
salen = sizeof(sa);
if (getpeername(fd, &sa.sa, &salen) < 0)
return -errno;
if (sa.sa.sa_family == AF_UNIX) {
struct ucred ucred;
/* UNIX connection sockets are anonymous, so let's use
* PID/UID as pretty credentials instead */
r = getpeercred(fd, &ucred);
if (r < 0)
return r;
if (asprintf(ret, "PID %lu/UID %lu", (unsigned long) ucred.pid, (unsigned long) ucred.pid) < 0)
return -ENOMEM;
return 0;
}
/* For remote sockets we translate IPv6 addresses back to IPv4
* if applicable, since that's nicer. */
return sockaddr_pretty(&sa.sa, salen, true, ret);
}
int getsockname_pretty(int fd, char **ret) {
union sockaddr_union sa;
socklen_t salen;
assert(fd >= 0);
assert(ret);
salen = sizeof(sa);
if (getsockname(fd, &sa.sa, &salen) < 0)
return -errno;
/* For local sockets we do not translate IPv6 addresses back
* to IPv6 if applicable, since this is usually used for
* listening sockets where the difference between IPv4 and
* IPv6 matters. */
return sockaddr_pretty(&sa.sa, salen, false, ret);
}
static const char* const netlink_family_table[] = {
[NETLINK_ROUTE] = "route",
[NETLINK_FIREWALL] = "firewall",
[NETLINK_INET_DIAG] = "inet-diag",
[NETLINK_NFLOG] = "nflog",
[NETLINK_XFRM] = "xfrm",
[NETLINK_SELINUX] = "selinux",
[NETLINK_ISCSI] = "iscsi",
[NETLINK_AUDIT] = "audit",
[NETLINK_FIB_LOOKUP] = "fib-lookup",
[NETLINK_CONNECTOR] = "connector",
[NETLINK_NETFILTER] = "netfilter",
[NETLINK_IP6_FW] = "ip6-fw",
[NETLINK_DNRTMSG] = "dnrtmsg",
[NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
[NETLINK_GENERIC] = "generic",
[NETLINK_SCSITRANSPORT] = "scsitransport",
[NETLINK_ECRYPTFS] = "ecryptfs"
};
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX);
static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
[SOCKET_ADDRESS_DEFAULT] = "default",
[SOCKET_ADDRESS_BOTH] = "both",
[SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
};
DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);