1 /*
2 * Copyright (C) 2010 Jens Georg <mail@jensge.org>
3 *
4 * Author: Jens Georg <mail@jensge.org>
5 *
6 * SPDX-License-Identifier: LGPL-2.1-or-later
7 *
8 */
9
10 #include <config.h>
11
12 #ifdef __APPLE__
13 #define __APPLE_USE_RFC_3542
14 #endif
15
16 #include "gssdp-error.h"
17 #include "gssdp-socket-functions.h"
18 #include "gssdp-pktinfo-message.h"
19
20 #include <errno.h>
21 #include <string.h>
22
23 #include <glib.h>
24
25 #ifdef G_OS_WIN32
26 #include <winsock2.h>
27 #include <ws2tcpip.h>
28 typedef int socklen_t;
29 #else
30 #include <sys/socket.h>
31 #include <sys/types.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 #endif
35
36 #include "gssdp-pktinfo6-message.h"
37 static char*
gssdp_socket_error_message(int error)38 gssdp_socket_error_message (int error) {
39 #ifdef G_OS_WIN32
40 return g_win32_error_message (error);
41 #else
42 return g_strdup (g_strerror (error));
43 #endif
44 }
45
46 static int
gssdp_socket_errno(void)47 gssdp_socket_errno (void) {
48 #ifdef G_OS_WIN32
49 return WSAGetLastError ();
50 #else
51 return errno;
52 #endif
53 }
54
55
56 static gboolean
gssdp_socket_option_set(GSocket * socket,int level,int option,const void * optval,socklen_t optlen,GError ** error)57 gssdp_socket_option_set (GSocket *socket,
58 int level,
59 int option,
60 const void *optval,
61 socklen_t optlen,
62 GError **error) {
63 int res;
64
65 res = setsockopt (g_socket_get_fd (socket),
66 level,
67 option,
68 optval,
69 optlen);
70
71 if (res == -1) {
72 char *message;
73 int error_code;
74
75 error_code = gssdp_socket_errno ();
76 message = gssdp_socket_error_message (error_code);
77 g_set_error_literal (error,
78 GSSDP_ERROR,
79 GSSDP_ERROR_FAILED,
80 message);
81 g_free (message);
82 }
83
84 return res != -1;
85 }
86
87 gboolean
gssdp_socket_mcast_interface_set(GSocket * socket,GInetAddress * iface_address,gint index,GError ** error)88 gssdp_socket_mcast_interface_set (GSocket *socket,
89 GInetAddress *iface_address,
90 gint index,
91 GError **error) {
92
93 const guint8 *address;
94 gsize native_size;
95
96 if (g_inet_address_get_family (iface_address) == G_SOCKET_FAMILY_IPV6) {
97 return gssdp_socket_option_set (socket,
98 IPPROTO_IPV6,
99 IPV6_MULTICAST_IF,
100 (char *)&index,
101 sizeof (index),
102 error);
103 } else {
104 address = g_inet_address_to_bytes (iface_address);
105 native_size = g_inet_address_get_native_size (iface_address);
106
107 return gssdp_socket_option_set (socket,
108 IPPROTO_IP,
109 IP_MULTICAST_IF,
110 (char *) address,
111 native_size,
112 error);
113 }
114 }
115
116 #define __GSSDP_UNUSED(x) (void)(x)
117
118 gboolean
gssdp_socket_reuse_address(GSocket * socket,gboolean enable,GError ** error)119 gssdp_socket_reuse_address (GSocket *socket,
120 gboolean enable,
121 GError **error) {
122 #if defined(G_OS_WIN32) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
123 return gssdp_socket_option_set (socket,
124 SOL_SOCKET,
125 #if defined(__OpenBSD__) || defined (__NetBSD__) || defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
126 SO_REUSEPORT,
127 #else
128 SO_REUSEADDR,
129 #endif
130
131 (char *) &enable,
132 sizeof (enable),
133 error);
134 #else
135 __GSSDP_UNUSED(socket);
136 __GSSDP_UNUSED(enable);
137 __GSSDP_UNUSED(error);
138 #endif
139 return TRUE;
140 }
141
142 gboolean
gssdp_socket_enable_info(GSocket * socket,GSocketFamily family,gboolean enable,GError ** error)143 gssdp_socket_enable_info (GSocket *socket,
144 GSocketFamily family,
145 gboolean enable,
146 GError **error)
147 {
148 #ifdef HAVE_PKTINFO
149 /* Register the type so g_socket_control_message_deserialize() will
150 * find it */
151 g_type_ensure (GSSDP_TYPE_PKTINFO_MESSAGE);
152 g_type_ensure (GSSDP_TYPE_PKTINFO6_MESSAGE);
153
154 if (family == G_SOCKET_FAMILY_IPV6) {
155 return gssdp_socket_option_set (socket,
156 IPPROTO_IPV6,
157 IPV6_RECVPKTINFO,
158 (char *)&enable,
159 sizeof (enable),
160 error);
161 } else if (family == G_SOCKET_FAMILY_IPV4) {
162 return gssdp_socket_option_set (socket,
163 IPPROTO_IP,
164 IP_PKTINFO,
165 (char *) &enable,
166 sizeof (enable),
167 error);
168 } else {
169 g_warning ("Invalid socket family: %d", family);
170
171 return FALSE;
172 }
173 #else
174 __GSSDP_UNUSED (socket);
175 __GSSDP_UNUSED (enable);
176 __GSSDP_UNUSED (error);
177
178 return TRUE;
179 #endif
180 }
181