1/***
2  This file is part of PulseAudio.
3
4  Copyright 2004-2006 Lennart Poettering
5  Copyright 2004 Joe Marcus Clarke
6  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
7
8  PulseAudio is free software; you can redistribute it and/or modify
9  it under the terms of the GNU Lesser General Public License as published
10  by the Free Software Foundation; either version 2.1 of the License,
11  or (at your option) any later version.
12
13  PulseAudio is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  General Public License for more details.
17
18  You should have received a copy of the GNU Lesser General Public License
19  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
20***/
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#include <stdlib.h>
27#include <signal.h>
28#include <errno.h>
29#include <string.h>
30#include <stdio.h>
31#include <sys/types.h>
32#include <unistd.h>
33#include <sys/stat.h>
34
35#ifdef HAVE_SYS_UN_H
36#include <sys/un.h>
37#endif
38#ifdef HAVE_NETINET_IN_H
39#include <netinet/in.h>
40#endif
41#ifdef HAVE_NETINET_IN_SYSTM_H
42#include <netinet/in_systm.h>
43#endif
44#ifdef HAVE_NETINET_IP_H
45#include <netinet/ip.h>
46#endif
47#ifdef HAVE_NETINET_TCP_H
48#include <netinet/tcp.h>
49#endif
50#ifdef HAVE_NETDB_H
51#include <netdb.h>
52#endif
53
54#include <pulsecore/core-error.h>
55#include <pulsecore/core-util.h>
56#include <pulsecore/log.h>
57#include <pulsecore/macro.h>
58#include <pulsecore/socket.h>
59#include <pulsecore/arpa-inet.h>
60
61#include "socket-util.h"
62
63void pa_socket_peer_to_string(int fd, char *c, size_t l) {
64#ifndef OS_IS_WIN32
65    struct stat st;
66#endif
67
68    pa_assert(fd >= 0);
69    pa_assert(c);
70    pa_assert(l > 0);
71
72#ifndef OS_IS_WIN32
73    pa_assert_se(fstat(fd, &st) == 0);
74
75    if (S_ISSOCK(st.st_mode))
76#endif
77    {
78        union {
79            struct sockaddr_storage storage;
80            struct sockaddr sa;
81            struct sockaddr_in in;
82#ifdef HAVE_IPV6
83            struct sockaddr_in6 in6;
84#endif
85#ifdef HAVE_SYS_UN_H
86            struct sockaddr_un un;
87#endif
88        } sa;
89        socklen_t sa_len = sizeof(sa);
90
91        if (getpeername(fd, &sa.sa, &sa_len) >= 0) {
92
93            if (sa.sa.sa_family == AF_INET) {
94                uint32_t ip = ntohl(sa.in.sin_addr.s_addr);
95
96                pa_snprintf(c, l, "TCP/IP client from %i.%i.%i.%i:%u",
97                            ip >> 24,
98                            (ip >> 16) & 0xFF,
99                            (ip >> 8) & 0xFF,
100                            ip & 0xFF,
101                            ntohs(sa.in.sin_port));
102                return;
103#ifdef HAVE_IPV6
104            } else if (sa.sa.sa_family == AF_INET6) {
105                char buf[INET6_ADDRSTRLEN];
106                const char *res;
107
108                res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf));
109                if (res) {
110                    pa_snprintf(c, l, "TCP/IP client from [%s]:%u", buf, ntohs(sa.in6.sin6_port));
111                    return;
112                }
113#endif
114#ifdef HAVE_SYS_UN_H
115            } else if (sa.sa.sa_family == AF_UNIX) {
116                pa_snprintf(c, l, "UNIX socket client");
117                return;
118#endif
119            }
120        }
121
122        pa_snprintf(c, l, "Unknown network client");
123        return;
124    }
125#ifndef OS_IS_WIN32
126    else if (S_ISCHR(st.st_mode) && (fd == 0 || fd == 1)) {
127        pa_snprintf(c, l, "STDIN/STDOUT client");
128        return;
129    }
130#endif /* OS_IS_WIN32 */
131
132    pa_snprintf(c, l, "Unknown client");
133}
134
135void pa_make_socket_low_delay(int fd) {
136
137#ifdef SO_PRIORITY
138    int priority;
139    pa_assert(fd >= 0);
140
141    priority = 6;
142    if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (const void *) &priority, sizeof(priority)) < 0)
143        pa_log_warn("SO_PRIORITY failed: %s", pa_cstrerror(errno));
144#endif
145}
146
147void pa_make_tcp_socket_low_delay(int fd) {
148    pa_assert(fd >= 0);
149
150    pa_make_socket_low_delay(fd);
151
152#if defined(SOL_TCP) || defined(IPPROTO_TCP)
153    {
154        int on = 1;
155#if defined(SOL_TCP)
156        if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *) &on, sizeof(on)) < 0)
157#else
158        if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *) &on, sizeof(on)) < 0)
159#endif
160            pa_log_warn("TCP_NODELAY failed: %s", pa_cstrerror(errno));
161    }
162#endif
163
164#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || defined(IPPROTO_IP))
165    {
166        int tos = IPTOS_LOWDELAY;
167#ifdef SOL_IP
168        if (setsockopt(fd, SOL_IP, IP_TOS, (const void *) &tos, sizeof(tos)) < 0)
169#else
170        if (setsockopt(fd, IPPROTO_IP, IP_TOS, (const void *) &tos, sizeof(tos)) < 0)
171#endif
172            pa_log_warn("IP_TOS failed: %s", pa_cstrerror(errno));
173    }
174#endif
175}
176
177void pa_make_udp_socket_low_delay(int fd) {
178    pa_assert(fd >= 0);
179
180    pa_make_socket_low_delay(fd);
181
182#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || defined(IPPROTO_IP))
183    {
184        int tos = IPTOS_LOWDELAY;
185#ifdef SOL_IP
186        if (setsockopt(fd, SOL_IP, IP_TOS, (const void *) &tos, sizeof(tos)) < 0)
187#else
188        if (setsockopt(fd, IPPROTO_IP, IP_TOS, (const void *) &tos, sizeof(tos)) < 0)
189#endif
190            pa_log_warn("IP_TOS failed: %s", pa_cstrerror(errno));
191    }
192#endif
193}
194
195int pa_socket_set_rcvbuf(int fd, size_t l) {
196    int bufsz = (int) l;
197
198    pa_assert(fd >= 0);
199
200    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const void *) &bufsz, sizeof(bufsz)) < 0) {
201        pa_log_warn("SO_RCVBUF: %s", pa_cstrerror(errno));
202        return -1;
203    }
204
205    return 0;
206}
207
208int pa_socket_set_sndbuf(int fd, size_t l) {
209    int bufsz = (int) l;
210
211    pa_assert(fd >= 0);
212
213    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &bufsz, sizeof(bufsz)) < 0) {
214        pa_log_warn("SO_SNDBUF: %s", pa_cstrerror(errno));
215        return -1;
216    }
217
218    return 0;
219}
220
221bool pa_socket_address_is_local(const struct sockaddr *sa) {
222    pa_assert(sa);
223
224    switch (sa->sa_family) {
225        case AF_UNIX:
226            return true;
227
228        case AF_INET:
229            return ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK;
230
231#ifdef HAVE_IPV6
232        case AF_INET6:
233            return memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0;
234#endif
235
236        default:
237            return false;
238    }
239}
240
241bool pa_socket_is_local(int fd) {
242
243    union {
244        struct sockaddr_storage storage;
245        struct sockaddr sa;
246        struct sockaddr_in in;
247#ifdef HAVE_IPV6
248        struct sockaddr_in6 in6;
249#endif
250#ifdef HAVE_SYS_UN_H
251        struct sockaddr_un un;
252#endif
253    } sa;
254    socklen_t sa_len = sizeof(sa);
255
256    if (getpeername(fd, &sa.sa, &sa_len) < 0)
257        return false;
258
259    return pa_socket_address_is_local(&sa.sa);
260}
261