uhttpd: Fix possible memory leaks when generating directory listing
[project/uhttpd.git] / relay.c
1 /*
2  * uhttpd - Tiny single-threaded httpd
3  *
4  *   Copyright (C) 2010-2013 Jo-Philipp Wich <xm@subsignal.org>
5  *   Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19
20 #include <signal.h>
21 #include "uhttpd.h"
22
23 void uh_relay_free(struct relay *r)
24 {
25         if (!r->cl)
26                 return;
27
28         if (r->proc.pending)
29                 kill(r->proc.pid, SIGKILL);
30
31         uloop_timeout_cancel(&r->timeout);
32         uloop_process_delete(&r->proc);
33         ustream_free(&r->sfd.stream);
34         close(r->sfd.fd.fd);
35
36         r->cl = NULL;
37 }
38
39 void uh_relay_close(struct relay *r, int ret)
40 {
41         struct ustream *us = &r->sfd.stream;
42
43         if (!us->notify_read)
44                 return;
45
46         us->notify_read = NULL;
47         us->notify_write = NULL;
48         us->notify_state = NULL;
49
50         if (r->close)
51                 r->close(r, ret);
52 }
53
54 static void relay_error(struct relay *r)
55 {
56         struct ustream *s = &r->sfd.stream;
57         int len;
58
59         r->error = true;
60         s->eof = true;
61         ustream_get_read_buf(s, &len);
62         if (len)
63                 ustream_consume(s, len);
64         ustream_state_change(s);
65 }
66
67 static void relay_process_headers(struct relay *r)
68 {
69         struct ustream *s = &r->sfd.stream;
70         char *buf, *newline;
71         int len;
72
73         if (!r->header_cb)
74                 return;
75
76         while (r->header_cb) {
77                 int line_len;
78                 char *val;
79
80                 buf = ustream_get_read_buf(s, &len);
81                 if (!buf || !len)
82                         break;
83
84                 newline = strchr(buf, '\n');
85                 if (!newline)
86                         break;
87
88                 line_len = newline + 1 - buf;
89                 if (newline > buf && newline[-1] == '\r')
90                         newline--;
91
92                 *newline = 0;
93                 if (newline == buf) {
94                         r->header_cb = NULL;
95                         if (r->header_end)
96                                 r->header_end(r);
97                         ustream_consume(s, line_len);
98                         break;
99                 }
100
101                 val = uh_split_header(buf);
102                 if (!val) {
103                         relay_error(r);
104                         return;
105                 }
106
107                 r->header_cb(r, buf, val);
108                 ustream_consume(s, line_len);
109         }
110 }
111
112 static void relay_read_cb(struct ustream *s, int bytes)
113 {
114         struct relay *r = container_of(s, struct relay, sfd.stream);
115         struct client *cl = r->cl;
116         struct ustream *us = cl->us;
117         char *buf;
118         int len;
119
120         if (r->process_done)
121                 uloop_timeout_set(&r->timeout, 1);
122
123         if (!r->error)
124                 relay_process_headers(r);
125
126         if (r->header_cb) {
127                 /*
128                  * if eof, ensure that remaining data is discarded, so the
129                  * state change cb will tear down the stream
130                  */
131                 if (s->eof)
132                         relay_error(r);
133                 return;
134         }
135
136         if (!s->eof && ustream_pending_data(us, true)) {
137                 ustream_set_read_blocked(s, true);
138                 return;
139         }
140
141         buf = ustream_get_read_buf(s, &len);
142         if (!buf || !len)
143                 return;
144
145         if (!r->skip_data)
146                 uh_chunk_write(cl, buf, len);
147
148         ustream_consume(s, len);
149 }
150
151 static void relay_close_if_done(struct uloop_timeout *timeout)
152 {
153         struct relay *r = container_of(timeout, struct relay, timeout);
154         struct ustream *s = &r->sfd.stream;
155
156         while (ustream_poll(&r->sfd.stream));
157
158         if (!(r->process_done || s->eof) || ustream_pending_data(s, false))
159                 return;
160
161         uh_relay_close(r, r->ret);
162 }
163
164 static void relay_state_cb(struct ustream *s)
165 {
166         struct relay *r = container_of(s, struct relay, sfd.stream);
167
168         if (r->process_done)
169                 uloop_timeout_set(&r->timeout, 1);
170 }
171
172 static void relay_proc_cb(struct uloop_process *proc, int ret)
173 {
174         struct relay *r = container_of(proc, struct relay, proc);
175
176         r->process_done = true;
177         r->ret = ret;
178         uloop_timeout_set(&r->timeout, 1);
179 }
180
181 void uh_relay_kill(struct client *cl, struct relay *r)
182 {
183         struct ustream *us = &r->sfd.stream;
184
185         kill(r->proc.pid, SIGKILL);
186         us->eof = true;
187         ustream_state_change(us);
188 }
189
190 void uh_relay_open(struct client *cl, struct relay *r, int fd, int pid)
191 {
192         struct ustream *us = &r->sfd.stream;
193
194         r->cl = cl;
195         us->notify_read = relay_read_cb;
196         us->notify_state = relay_state_cb;
197         us->string_data = true;
198         ustream_fd_init(&r->sfd, fd);
199
200         r->proc.pid = pid;
201         r->proc.cb = relay_proc_cb;
202         uloop_process_add(&r->proc);
203
204         r->timeout.cb = relay_close_if_done;
205 }