1 /*
2 * Copyright (c) 2002-2019 Balabit
3 * Copyright (c) 1998-2019 Balázs Scheidler
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * As an additional exemption you are allowed to compile & link against the
20 * OpenSSL libraries as published by the OpenSSL project. See the file
21 * COPYING for details.
22 *
23 */
24 #include "transport-udp-socket.h"
25 #include "transport/transport-socket.h"
26 #include "gsocket.h"
27 #include "scratch-buffers.h"
28 #include "str-format.h"
29
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <errno.h>
33
34 typedef struct _LogTransportUDP LogTransportUDP;
35 struct _LogTransportUDP
36 {
37 LogTransportSocket super;
38 GSockAddr *bind_addr;
39 };
40
41 #if defined(SYSLOG_NG_HAVE_CTRLBUF_IN_MSGHDR)
42
43 #if defined(__DragonFly__) || defined(__OpenBSD__)
44
45 GSockAddr *
_extract_dest_ip4_addr_from_cmsg(struct cmsghdr * cmsg,GSockAddr * bind_addr)46 _extract_dest_ip4_addr_from_cmsg(struct cmsghdr *cmsg, GSockAddr *bind_addr)
47 {
48 if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR)
49 {
50 struct sockaddr_in sin = *(struct sockaddr_in *) &bind_addr->sa;
51
52 struct in_addr *sin_addr = (struct in_addr *) CMSG_DATA(cmsg);
53 sin.sin_addr = *sin_addr;
54
55 return g_sockaddr_new((struct sockaddr *) &sin, sizeof(sin));
56 }
57 return NULL;
58 }
59
60 #else
61
62 GSockAddr *
_extract_dest_ip4_addr_from_cmsg(struct cmsghdr * cmsg,GSockAddr * bind_addr)63 _extract_dest_ip4_addr_from_cmsg(struct cmsghdr *cmsg, GSockAddr *bind_addr)
64 {
65 if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO)
66 {
67 struct sockaddr_in sin;
68 struct in_pktinfo *inpkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
69
70 /* we need to copy the port number from the bind address as it is not
71 * part of IP_PKTINFO */
72
73 sin = *(struct sockaddr_in *) &bind_addr->sa;
74 sin.sin_addr = inpkt->ipi_addr;
75 return g_sockaddr_new((struct sockaddr *) &sin, sizeof(sin));
76 }
77 return NULL;
78 }
79 #endif
80
81 #if SYSLOG_NG_ENABLE_IPV6
82
83 GSockAddr *
_extract_dest_ip6_addr_from_cmsg(struct cmsghdr * cmsg,GSockAddr * bind_addr)84 _extract_dest_ip6_addr_from_cmsg(struct cmsghdr *cmsg, GSockAddr *bind_addr)
85 {
86 if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO)
87 {
88 struct sockaddr_in6 sin6;
89 struct in6_pktinfo *in6pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
90
91 /* we need to copy the port number (and scope id) from the bind
92 * address as it is not part of IPV6_PKTINFO */
93
94 sin6 = *(struct sockaddr_in6 *) &bind_addr->sa;
95 sin6.sin6_addr = in6pkt->ipi6_addr;
96 return g_sockaddr_new((struct sockaddr *) &sin6, sizeof(sin6));
97 }
98 return NULL;
99 }
100
101 #endif
102
103 GSockAddr *
_extract_dest_addr_from_cmsg(struct cmsghdr * cmsg,GSockAddr * bind_addr)104 _extract_dest_addr_from_cmsg(struct cmsghdr *cmsg, GSockAddr *bind_addr)
105 {
106 if (bind_addr->sa.sa_family == AF_INET)
107 return _extract_dest_ip4_addr_from_cmsg(cmsg, bind_addr);
108 #if SYSLOG_NG_ENABLE_IPV6
109 else if (bind_addr->sa.sa_family == AF_INET6)
110 return _extract_dest_ip6_addr_from_cmsg(cmsg, bind_addr);
111 #endif
112 else
113 g_assert_not_reached();
114 }
115
116 static void
_feed_aux_from_cmsg(LogTransportUDP * self,LogTransportAuxData * aux,struct msghdr * msg)117 _feed_aux_from_cmsg(LogTransportUDP *self, LogTransportAuxData *aux, struct msghdr *msg)
118 {
119 struct cmsghdr *cmsg;
120
121 for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg))
122 {
123 GSockAddr *dest_addr;
124
125 dest_addr = _extract_dest_addr_from_cmsg(cmsg, self->bind_addr);
126 if (dest_addr)
127 {
128 log_transport_aux_data_set_local_addr_ref(aux, dest_addr);
129 break;
130 }
131 }
132 }
133
134 #else
135 #define _feed_aux_from_cmsg(self, aux, msg)
136 #endif
137
138
139 static gssize
log_transport_udp_socket_read_method(LogTransport * s,gpointer buf,gsize buflen,LogTransportAuxData * aux)140 log_transport_udp_socket_read_method(LogTransport *s, gpointer buf, gsize buflen, LogTransportAuxData *aux)
141 {
142 LogTransportUDP *self = (LogTransportUDP *) s;
143 gint rc;
144 struct msghdr msg;
145 struct iovec iov[1];
146 struct sockaddr_storage ss;
147 #if defined(SYSLOG_NG_HAVE_CTRLBUF_IN_MSGHDR)
148 gchar ctlbuf[64];
149 msg.msg_control = ctlbuf;
150 msg.msg_controllen = sizeof(ctlbuf);
151 #endif
152
153 msg.msg_name = (struct sockaddr *) &ss;
154 msg.msg_namelen = sizeof(ss);
155 msg.msg_iovlen = 1;
156 msg.msg_iov = iov;
157 iov[0].iov_base = buf;
158 iov[0].iov_len = buflen;
159
160 do
161 {
162 rc = recvmsg(self->super.super.fd, &msg, 0);
163 }
164 while (rc == -1 && errno == EINTR);
165
166 if (rc == 0)
167 {
168 /* DGRAM sockets should never return EOF, they just need to be read again */
169 rc = -1;
170 errno = EAGAIN;
171 }
172 else if (rc > 0)
173 {
174 if (msg.msg_namelen && aux)
175 log_transport_aux_data_set_peer_addr_ref(aux, g_sockaddr_new((struct sockaddr *) &ss, msg.msg_namelen));
176 if (aux)
177 aux->proto = self->super.proto;
178 _feed_aux_from_cmsg(self, aux, &msg);
179 }
180 return rc;
181
182 }
183
184 static void
log_transport_udp_setup_fd(LogTransportUDP * self,gint fd)185 log_transport_udp_setup_fd(LogTransportUDP *self, gint fd)
186 {
187 gint on = 1;
188
189 self->bind_addr = g_socket_get_local_name(fd);
190
191 if (self->super.address_family == AF_INET)
192 {
193 #if defined(__DragonFly__) || defined(__OpenBSD__)
194 setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on));
195 #else
196 setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
197 #endif
198 }
199 #if SYSLOG_NG_ENABLE_IPV6
200 else if (self->bind_addr->sa.sa_family == AF_INET6)
201 setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
202 #endif
203 else
204 g_assert_not_reached();
205
206 }
207
208 static void
log_transport_udp_socket_free(LogTransport * s)209 log_transport_udp_socket_free(LogTransport *s)
210 {
211 LogTransportUDP *self = (LogTransportUDP *)s;
212 g_sockaddr_unref(self->bind_addr);
213 log_transport_free_method(s);
214 }
215
216 LogTransport *
log_transport_udp_socket_new(gint fd)217 log_transport_udp_socket_new(gint fd)
218 {
219 LogTransportUDP *self = g_new0(LogTransportUDP, 1);
220
221 log_transport_dgram_socket_init_instance(&self->super, fd);
222 self->super.super.read = log_transport_udp_socket_read_method;
223 self->super.super.free_fn = log_transport_udp_socket_free;
224
225 log_transport_udp_setup_fd(self, fd);
226 return &self->super.super;
227 }
228