]> git.ipfire.org Git - thirdparty/squid.git/blob - src/log/ModUdp.cc
Maintenance: Removed most NULLs using modernize-use-nullptr (#1075)
[thirdparty/squid.git] / src / log / ModUdp.cc
1 /*
2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /* DEBUG: section 50 Log file handling */
10
11 #include "squid.h"
12 #include "comm.h"
13 #include "comm/Connection.h"
14 #include "fatal.h"
15 #include "fd.h"
16 #include "fs_io.h"
17 #include "log/File.h"
18 #include "log/ModUdp.h"
19 #include "Parsing.h"
20 #include "SquidConfig.h"
21
22 #include <cerrno>
23
24 /*
25 * This logfile UDP module is mostly inspired by a patch by Tim Starling
26 * from Wikimedia.
27 *
28 * It doesn't do any UDP buffering - it'd be quite a bit of work for
29 * something which the kernel could be doing for you!
30 */
31
32 typedef struct {
33 int fd;
34 char *buf;
35 size_t bufsz;
36 int offset;
37 } l_udp_t;
38
39 static void
40 logfile_mod_udp_write(Logfile * lf, const char *buf, size_t len)
41 {
42 l_udp_t *ll = (l_udp_t *) lf->data;
43 ssize_t s;
44 s = write(ll->fd, (char const *) buf, len);
45 fd_bytes(ll->fd, s, FD_WRITE);
46 #if 0
47 // TODO: Enable after polishing to properly log these errors.
48 if (s < 0) {
49 int xerrno = errno;
50 debugs(1, DBG_IMPORTANT, "logfile (udp): got errno (" << errno << "):" << xstrerr(xerrno));
51 }
52 if (s != len) {
53 debugs(1, DBG_IMPORTANT, "logfile (udp): len=" << len << ", wrote=" << s);
54 }
55 #endif
56
57 /* We don't worry about network errors for now */
58 }
59
60 static void
61 logfile_mod_udp_flush(Logfile * lf)
62 {
63 l_udp_t *ll = (l_udp_t *) lf->data;
64 if (0 == ll->offset)
65 return;
66 logfile_mod_udp_write(lf, ll->buf, (size_t) ll->offset);
67 ll->offset = 0;
68 }
69
70 static void
71 logfile_mod_udp_writeline(Logfile * lf, const char *buf, size_t len)
72 {
73 l_udp_t *ll = (l_udp_t *) lf->data;
74
75 if (0 == ll->bufsz) {
76 /* buffering disabled */
77 logfile_mod_udp_write(lf, buf, len);
78 return;
79 }
80 if (ll->offset > 0 && (ll->offset + len + 4) > ll->bufsz)
81 logfile_mod_udp_flush(lf);
82
83 if (len > ll->bufsz) {
84 /* too big to fit in buffer */
85 logfile_mod_udp_write(lf, buf, len);
86 return;
87 }
88 /* buffer it */
89 memcpy(ll->buf + ll->offset, buf, len);
90
91 ll->offset += len;
92
93 assert(ll->offset >= 0);
94
95 assert((size_t) ll->offset <= ll->bufsz);
96 }
97
98 static void
99 logfile_mod_udp_linestart(Logfile *)
100 {
101 }
102
103 static void
104 logfile_mod_udp_lineend(Logfile *)
105 {
106 }
107
108 static void
109 logfile_mod_udp_rotate(Logfile *, const int16_t)
110 {
111 }
112
113 static void
114 logfile_mod_udp_close(Logfile * lf)
115 {
116 l_udp_t *ll = (l_udp_t *) lf->data;
117 lf->f_flush(lf);
118
119 if (ll->fd >= 0)
120 file_close(ll->fd);
121
122 if (ll->buf)
123 xfree(ll->buf);
124
125 xfree(lf->data);
126 lf->data = nullptr;
127 }
128
129 /*
130 * This code expects the path to be //host:port
131 */
132 int
133 logfile_mod_udp_open(Logfile * lf, const char *path, size_t bufsz, int fatal_flag)
134 {
135 Ip::Address addr;
136 char *strAddr;
137
138 lf->f_close = logfile_mod_udp_close;
139 lf->f_linewrite = logfile_mod_udp_writeline;
140 lf->f_linestart = logfile_mod_udp_linestart;
141 lf->f_lineend = logfile_mod_udp_lineend;
142 lf->f_flush = logfile_mod_udp_flush;
143 lf->f_rotate = logfile_mod_udp_rotate;
144
145 l_udp_t *ll = static_cast<l_udp_t*>(xcalloc(1, sizeof(*ll)));
146 lf->data = ll;
147
148 if (strncmp(path, "//", 2) == 0) {
149 path += 2;
150 }
151 strAddr = xstrdup(path);
152 if (!GetHostWithPort(strAddr, &addr)) {
153 if (lf->flags.fatal) {
154 fatalf("Invalid UDP logging address '%s'\n", lf->path);
155 } else {
156 debugs(50, DBG_IMPORTANT, "ERROR: Invalid UDP logging address '" << lf->path << "'");
157 safe_free(strAddr);
158 return FALSE;
159 }
160 }
161 safe_free(strAddr);
162
163 Ip::Address any_addr;
164 any_addr.setAnyAddr();
165
166 // require the sending UDP port to be of the right family for the destination address.
167 if (addr.isIPv4())
168 any_addr.setIPv4();
169
170 ll->fd = comm_open(SOCK_DGRAM, IPPROTO_UDP, any_addr, COMM_NONBLOCKING, "UDP log socket");
171 int xerrno = errno;
172 if (ll->fd < 0) {
173 if (lf->flags.fatal) {
174 fatalf("Unable to open UDP socket for logging\n");
175 } else {
176 debugs(50, DBG_IMPORTANT, "ERROR: Unable to open UDP socket for logging");
177 return FALSE;
178 }
179 } else if (!comm_connect_addr(ll->fd, addr)) {
180 xerrno = errno;
181 if (lf->flags.fatal) {
182 fatalf("Unable to connect to %s for UDP log: %s\n", lf->path, xstrerr(xerrno));
183 } else {
184 debugs(50, DBG_IMPORTANT, "ERROR: Unable to connect to " << lf->path << " for UDP log: " << xstrerr(xerrno));
185 return FALSE;
186 }
187 }
188 if (ll->fd == -1) {
189 if (ENOENT == xerrno && fatal_flag) {
190 fatalf("Cannot open '%s' because\n"
191 "\tthe parent directory does not exist.\n"
192 "\tPlease create the directory.\n", path);
193 } else if (EACCES == xerrno && fatal_flag) {
194 fatalf("Cannot open '%s' for writing.\n"
195 "\tThe parent directory must be writeable by the\n"
196 "\tuser '%s', which is the cache_effective_user\n"
197 "\tset in squid.conf.", path, Config.effectiveUser);
198 } else {
199 debugs(50, DBG_IMPORTANT, "logfileOpen (UDP): " << lf->path << ": " << xstrerr(xerrno));
200 return 0;
201 }
202 }
203 /* Force buffer size to something roughly fitting inside an MTU */
204 /*
205 * XXX note the receive side needs to receive the whole packet at once;
206 * applications like netcat have a small default receive buffer and will
207 * truncate!
208 */
209 if (bufsz > 1400)
210 bufsz = 1400;
211 if (bufsz > 0) {
212 ll->buf = static_cast<char*>(xmalloc(bufsz));
213 ll->bufsz = bufsz;
214 }
215
216 return 1;
217 }
218