1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_event.h>
11 
12 
13 static ngx_chain_t *ngx_udp_output_chain_to_iovec(ngx_iovec_t *vec,
14     ngx_chain_t *in, ngx_log_t *log);
15 static ssize_t ngx_sendmsg(ngx_connection_t *c, ngx_iovec_t *vec);
16 
17 
18 ngx_chain_t *
ngx_udp_unix_sendmsg_chain(ngx_connection_t * c,ngx_chain_t * in,off_t limit)19 ngx_udp_unix_sendmsg_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
20 {
21     ssize_t        n;
22     off_t          send;
23     ngx_chain_t   *cl;
24     ngx_event_t   *wev;
25     ngx_iovec_t    vec;
26     struct iovec   iovs[NGX_IOVS_PREALLOCATE];
27 
28     wev = c->write;
29 
30     if (!wev->ready) {
31         return in;
32     }
33 
34 #if (NGX_HAVE_KQUEUE) || (NGX_HAVE_FSTACK)
35 
36     if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
37         (void) ngx_connection_error(c, wev->kq_errno,
38                                "kevent() reported about an closed connection");
39         wev->error = 1;
40         return NGX_CHAIN_ERROR;
41     }
42 
43 #endif
44 
45     /* the maximum limit size is the maximum size_t value - the page size */
46 
47     if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
48         limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
49     }
50 
51     send = 0;
52 
53     vec.iovs = iovs;
54     vec.nalloc = NGX_IOVS_PREALLOCATE;
55 
56     for ( ;; ) {
57 
58         /* create the iovec and coalesce the neighbouring bufs */
59 
60         cl = ngx_udp_output_chain_to_iovec(&vec, in, c->log);
61 
62         if (cl == NGX_CHAIN_ERROR) {
63             return NGX_CHAIN_ERROR;
64         }
65 
66         if (cl && cl->buf->in_file) {
67             ngx_log_error(NGX_LOG_ALERT, c->log, 0,
68                           "file buf in sendmsg "
69                           "t:%d r:%d f:%d %p %p-%p %p %O-%O",
70                           cl->buf->temporary,
71                           cl->buf->recycled,
72                           cl->buf->in_file,
73                           cl->buf->start,
74                           cl->buf->pos,
75                           cl->buf->last,
76                           cl->buf->file,
77                           cl->buf->file_pos,
78                           cl->buf->file_last);
79 
80             ngx_debug_point();
81 
82             return NGX_CHAIN_ERROR;
83         }
84 
85         if (cl == in) {
86             return in;
87         }
88 
89         send += vec.size;
90 
91         n = ngx_sendmsg(c, &vec);
92 
93         if (n == NGX_ERROR) {
94             return NGX_CHAIN_ERROR;
95         }
96 
97         if (n == NGX_AGAIN) {
98             wev->ready = 0;
99             return in;
100         }
101 
102         c->sent += n;
103 
104         in = ngx_chain_update_sent(in, n);
105 
106         if (send >= limit || in == NULL) {
107             return in;
108         }
109     }
110 }
111 
112 
113 static ngx_chain_t *
ngx_udp_output_chain_to_iovec(ngx_iovec_t * vec,ngx_chain_t * in,ngx_log_t * log)114 ngx_udp_output_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *in, ngx_log_t *log)
115 {
116     size_t         total, size;
117     u_char        *prev;
118     ngx_uint_t     n, flush;
119     ngx_chain_t   *cl;
120     struct iovec  *iov;
121 
122     cl = in;
123     iov = NULL;
124     prev = NULL;
125     total = 0;
126     n = 0;
127     flush = 0;
128 
129     for ( /* void */ ; in && !flush; in = in->next) {
130 
131         if (in->buf->flush || in->buf->last_buf) {
132             flush = 1;
133         }
134 
135         if (ngx_buf_special(in->buf)) {
136             continue;
137         }
138 
139         if (in->buf->in_file) {
140             break;
141         }
142 
143         if (!ngx_buf_in_memory(in->buf)) {
144             ngx_log_error(NGX_LOG_ALERT, log, 0,
145                           "bad buf in output chain "
146                           "t:%d r:%d f:%d %p %p-%p %p %O-%O",
147                           in->buf->temporary,
148                           in->buf->recycled,
149                           in->buf->in_file,
150                           in->buf->start,
151                           in->buf->pos,
152                           in->buf->last,
153                           in->buf->file,
154                           in->buf->file_pos,
155                           in->buf->file_last);
156 
157             ngx_debug_point();
158 
159             return NGX_CHAIN_ERROR;
160         }
161 
162         size = in->buf->last - in->buf->pos;
163 
164         if (prev == in->buf->pos) {
165             iov->iov_len += size;
166 
167         } else {
168             if (n == vec->nalloc) {
169                 ngx_log_error(NGX_LOG_ALERT, log, 0,
170                               "too many parts in a datagram");
171                 return NGX_CHAIN_ERROR;
172             }
173 
174             iov = &vec->iovs[n++];
175 
176             iov->iov_base = (void *) in->buf->pos;
177             iov->iov_len = size;
178         }
179 
180         prev = in->buf->pos + size;
181         total += size;
182     }
183 
184     if (!flush) {
185 #if (NGX_SUPPRESS_WARN)
186         vec->size = 0;
187         vec->count = 0;
188 #endif
189         return cl;
190     }
191 
192     vec->count = n;
193     vec->size = total;
194 
195     return in;
196 }
197 
198 
199 static ssize_t
ngx_sendmsg(ngx_connection_t * c,ngx_iovec_t * vec)200 ngx_sendmsg(ngx_connection_t *c, ngx_iovec_t *vec)
201 {
202     ssize_t        n;
203     ngx_err_t      err;
204     struct msghdr  msg;
205 
206 #if (NGX_HAVE_MSGHDR_MSG_CONTROL)
207 
208 #if (NGX_HAVE_IP_SENDSRCADDR)
209     u_char         msg_control[CMSG_SPACE(sizeof(struct in_addr))];
210 #elif (NGX_HAVE_IP_PKTINFO) && (!NGX_HAVE_FSTACK)
211     u_char         msg_control[CMSG_SPACE(sizeof(struct in_pktinfo))];
212 #endif
213 
214 #if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO && !NGX_HAVE_FSTACK)
215     u_char         msg_control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
216 #endif
217 
218 #endif
219 
220     ngx_memzero(&msg, sizeof(struct msghdr));
221 
222     if (c->socklen) {
223         msg.msg_name = c->sockaddr;
224         msg.msg_namelen = c->socklen;
225     }
226 
227     msg.msg_iov = vec->iovs;
228     msg.msg_iovlen = vec->count;
229 
230 #if (NGX_HAVE_MSGHDR_MSG_CONTROL)
231 
232     if (c->listening && c->listening->wildcard && c->local_sockaddr) {
233 
234 #if (NGX_HAVE_IP_SENDSRCADDR)
235 
236         if (c->local_sockaddr->sa_family == AF_INET) {
237             struct cmsghdr      *cmsg;
238             struct in_addr      *addr;
239             struct sockaddr_in  *sin;
240 
241             msg.msg_control = &msg_control;
242             msg.msg_controllen = sizeof(msg_control);
243 
244             cmsg = CMSG_FIRSTHDR(&msg);
245             cmsg->cmsg_level = IPPROTO_IP;
246             cmsg->cmsg_type = IP_SENDSRCADDR;
247             cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
248 
249             sin = (struct sockaddr_in *) c->local_sockaddr;
250 
251             addr = (struct in_addr *) CMSG_DATA(cmsg);
252             *addr = sin->sin_addr;
253         }
254 
255 #elif (NGX_HAVE_IP_PKTINFO) && (!NGX_HAVE_FSTACK)
256 
257         if (c->local_sockaddr->sa_family == AF_INET) {
258             struct cmsghdr      *cmsg;
259             struct in_pktinfo   *pkt;
260             struct sockaddr_in  *sin;
261 
262             msg.msg_control = &msg_control;
263             msg.msg_controllen = sizeof(msg_control);
264 
265             cmsg = CMSG_FIRSTHDR(&msg);
266             cmsg->cmsg_level = IPPROTO_IP;
267             cmsg->cmsg_type = IP_PKTINFO;
268             cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
269 
270             sin = (struct sockaddr_in *) c->local_sockaddr;
271 
272             pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
273             ngx_memzero(pkt, sizeof(struct in_pktinfo));
274             pkt->ipi_spec_dst = sin->sin_addr;
275         }
276 
277 #endif
278 
279 #if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO && !NGX_HAVE_FSTACK)
280 
281         if (c->local_sockaddr->sa_family == AF_INET6) {
282             struct cmsghdr       *cmsg;
283             struct in6_pktinfo   *pkt6;
284             struct sockaddr_in6  *sin6;
285 
286             msg.msg_control = &msg_control6;
287             msg.msg_controllen = sizeof(msg_control6);
288 
289             cmsg = CMSG_FIRSTHDR(&msg);
290             cmsg->cmsg_level = IPPROTO_IPV6;
291             cmsg->cmsg_type = IPV6_PKTINFO;
292             cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
293 
294             sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
295 
296             pkt6 = (struct in6_pktinfo *) CMSG_DATA(cmsg);
297             ngx_memzero(pkt6, sizeof(struct in6_pktinfo));
298             pkt6->ipi6_addr = sin6->sin6_addr;
299         }
300 
301 #endif
302     }
303 
304 #endif
305 
306 eintr:
307 
308     n = sendmsg(c->fd, &msg, 0);
309 
310     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
311                    "sendmsg: %z of %uz", n, vec->size);
312 
313     if (n == -1) {
314         err = ngx_errno;
315 
316         switch (err) {
317         case NGX_EAGAIN:
318             ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
319                            "sendmsg() not ready");
320             return NGX_AGAIN;
321 
322         case NGX_EINTR:
323             ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
324                            "sendmsg() was interrupted");
325             goto eintr;
326 
327         default:
328             c->write->error = 1;
329             ngx_connection_error(c, err, "sendmsg() failed");
330             return NGX_ERROR;
331         }
332     }
333 
334     return n;
335 }
336