diff options
Diffstat (limited to 'ot_http.c')
-rw-r--r-- | ot_http.c | 837 |
1 files changed, 478 insertions, 359 deletions
@@ -4,531 +4,645 @@ | |||
4 | $id$ */ | 4 | $id$ */ |
5 | 5 | ||
6 | /* System */ | 6 | /* System */ |
7 | #include <sys/types.h> | 7 | #define _GNU_SOURCE |
8 | #include <arpa/inet.h> | 8 | #include <arpa/inet.h> |
9 | #include <stdlib.h> | 9 | #include <pthread.h> |
10 | #include <stdio.h> | 10 | #include <stdio.h> |
11 | #include <stdlib.h> | ||
11 | #include <string.h> | 12 | #include <string.h> |
13 | #include <sys/types.h> | ||
12 | #include <unistd.h> | 14 | #include <unistd.h> |
13 | #include <pthread.h> | ||
14 | 15 | ||
15 | /* Libowfat */ | 16 | /* Libowfat */ |
16 | #include "byte.h" | ||
17 | #include "array.h" | 17 | #include "array.h" |
18 | #include "byte.h" | ||
19 | #include "case.h" | ||
18 | #include "iob.h" | 20 | #include "iob.h" |
19 | #include "ip6.h" | 21 | #include "ip6.h" |
20 | #include "scan.h" | 22 | #include "scan.h" |
21 | #include "case.h" | ||
22 | 23 | ||
23 | /* Opentracker */ | 24 | /* Opentracker */ |
24 | #include "trackerlogic.h" | 25 | #include "ot_accesslist.h" |
25 | #include "ot_mutex.h" | 26 | #include "ot_fullscrape.h" |
26 | #include "ot_http.h" | 27 | #include "ot_http.h" |
27 | #include "ot_iovec.h" | 28 | #include "ot_iovec.h" |
28 | #include "scan_urlencoded_query.h" | 29 | #include "ot_mutex.h" |
29 | #include "ot_fullscrape.h" | ||
30 | #include "ot_stats.h" | 30 | #include "ot_stats.h" |
31 | #include "ot_accesslist.h" | 31 | #include "scan_urlencoded_query.h" |
32 | #include "trackerlogic.h" | ||
33 | |||
34 | #ifdef WANT_NO_AUTO_FREE | ||
35 | #define OT_IOB_INIT(B) bzero(B, sizeof(io_batch)) | ||
36 | #else | ||
37 | #define OT_IOB_INIT(B) iob_init_autofree(B, 0) | ||
38 | #endif | ||
32 | 39 | ||
33 | #define OT_MAXMULTISCRAPE_COUNT 64 | 40 | #define OT_MAXMULTISCRAPE_COUNT 64 |
34 | #define OT_BATCH_LIMIT (1024*1024*16) | 41 | #define OT_BATCH_LIMIT (1024 * 1024 * 16) |
35 | extern char *g_redirecturl; | 42 | extern char *g_redirecturl; |
36 | 43 | ||
37 | char *g_stats_path; | 44 | char *g_stats_path; |
38 | ssize_t g_stats_path_len; | 45 | ssize_t g_stats_path_len; |
39 | 46 | ||
40 | enum { | 47 | enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 }; |
41 | SUCCESS_HTTP_HEADER_LENGTH = 80, | ||
42 | SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING = 32, | ||
43 | SUCCESS_HTTP_SIZE_OFF = 17 }; | ||
44 | 48 | ||
45 | static void http_senddata( const int64 sock, struct ot_workstruct *ws ) { | 49 | static void http_senddata(const int64 sock, struct ot_workstruct *ws) { |
46 | struct http_data *cookie = io_getcookie( sock ); | 50 | struct http_data *cookie = io_getcookie(sock); |
47 | ssize_t written_size; | 51 | ssize_t written_size; |
48 | 52 | ||
49 | if( !cookie ) { io_close(sock); return; } | 53 | if (!cookie) { |
54 | io_close(sock); | ||
55 | return; | ||
56 | } | ||
50 | 57 | ||
51 | /* whoever sends data is not interested in its input-array */ | 58 | /* whoever sends data is not interested in its input-array */ |
52 | if( ws->keep_alive && ws->header_size != ws->request_size ) { | 59 | if (ws->keep_alive && ws->header_size != ws->request_size) { |
53 | size_t rest = ws->request_size - ws->header_size; | 60 | size_t rest = ws->request_size - ws->header_size; |
54 | if( array_start(&cookie->request) ) { | 61 | if (array_start(&cookie->request)) { |
55 | memmove( array_start(&cookie->request), ws->request + ws->header_size, rest ); | 62 | memmove(array_start(&cookie->request), ws->request + ws->header_size, rest); |
56 | array_truncate( &cookie->request, 1, rest ); | 63 | array_truncate(&cookie->request, 1, rest); |
57 | } else | 64 | } else |
58 | array_catb(&cookie->request, ws->request + ws->header_size, rest ); | 65 | array_catb(&cookie->request, ws->request + ws->header_size, rest); |
59 | } else | 66 | } else |
60 | array_reset( &cookie->request ); | 67 | array_reset(&cookie->request); |
61 | 68 | ||
62 | written_size = write( sock, ws->reply, ws->reply_size ); | 69 | written_size = write(sock, ws->reply, ws->reply_size); |
63 | if( ( written_size < 0 ) || ( ( written_size == ws->reply_size ) && !ws->keep_alive ) ) { | 70 | if ((written_size < 0) || ((written_size == ws->reply_size) && !ws->keep_alive)) { |
64 | array_reset( &cookie->request ); | 71 | array_reset(&cookie->request); |
65 | free( cookie ); io_close( sock ); return; | 72 | free(cookie); |
73 | io_close(sock); | ||
74 | return; | ||
66 | } | 75 | } |
67 | 76 | ||
68 | if( written_size < ws->reply_size ) { | 77 | if (written_size < ws->reply_size) { |
69 | char * outbuf; | 78 | char *outbuf; |
70 | tai6464 t; | 79 | tai6464 t; |
71 | 80 | ||
72 | if( !( outbuf = malloc( ws->reply_size - written_size ) ) ) { | 81 | if (!(outbuf = malloc(ws->reply_size - written_size))) { |
73 | array_reset( &cookie->request ); | 82 | array_reset(&cookie->request); |
74 | free(cookie); io_close( sock ); | 83 | free(cookie); |
84 | io_close(sock); | ||
75 | return; | 85 | return; |
76 | } | 86 | } |
77 | 87 | ||
78 | memcpy( outbuf, ws->reply + written_size, ws->reply_size - written_size ); | 88 | memcpy(outbuf, ws->reply + written_size, ws->reply_size - written_size); |
79 | if ( !cookie->batch ) { | 89 | if (!cookie->batch) { |
80 | cookie->batch = malloc( sizeof(io_batch) ); | 90 | cookie->batch = malloc(sizeof(io_batch)); |
81 | memset( cookie->batch, 0, sizeof(io_batch) ); | 91 | OT_IOB_INIT(cookie->batch); |
82 | cookie->batches = 1; | 92 | cookie->batches = 1; |
83 | } | 93 | } |
84 | 94 | ||
85 | iob_addbuf_free( cookie->batch, outbuf, ws->reply_size - written_size ); | 95 | iob_addbuf_free(cookie->batch, outbuf, ws->reply_size - written_size); |
86 | 96 | ||
87 | /* writeable short data sockets just have a tcp timeout */ | 97 | /* writeable short data sockets just have a tcp timeout */ |
88 | if( !ws->keep_alive ) { | 98 | if (!ws->keep_alive) { |
89 | taia_uint( &t, 0 ); io_timeout( sock, t ); | 99 | taia_uint(&t, 0); |
90 | io_dontwantread( sock ); | 100 | io_timeout(sock, t); |
101 | io_dontwantread(sock); | ||
91 | } | 102 | } |
92 | io_wantwrite( sock ); | 103 | io_wantwrite(sock); |
93 | } | 104 | } |
94 | } | 105 | } |
95 | 106 | ||
96 | #define HTTPERROR_302 return http_issue_error( sock, ws, CODE_HTTPERROR_302 ) | 107 | #define HTTPERROR_302 return http_issue_error(sock, ws, CODE_HTTPERROR_302) |
97 | #define HTTPERROR_400 return http_issue_error( sock, ws, CODE_HTTPERROR_400 ) | 108 | #define HTTPERROR_400 return http_issue_error(sock, ws, CODE_HTTPERROR_400) |
98 | #define HTTPERROR_400_PARAM return http_issue_error( sock, ws, CODE_HTTPERROR_400_PARAM ) | 109 | #define HTTPERROR_400_PARAM return http_issue_error(sock, ws, CODE_HTTPERROR_400_PARAM) |
99 | #define HTTPERROR_400_COMPACT return http_issue_error( sock, ws, CODE_HTTPERROR_400_COMPACT ) | 110 | #define HTTPERROR_400_COMPACT return http_issue_error(sock, ws, CODE_HTTPERROR_400_COMPACT) |
100 | #define HTTPERROR_400_DOUBLEHASH return http_issue_error( sock, ws, CODE_HTTPERROR_400_PARAM ) | 111 | #define HTTPERROR_400_DOUBLEHASH return http_issue_error(sock, ws, CODE_HTTPERROR_400_PARAM) |
101 | #define HTTPERROR_402_NOTMODEST return http_issue_error( sock, ws, CODE_HTTPERROR_402_NOTMODEST ) | 112 | #define HTTPERROR_402_NOTMODEST return http_issue_error(sock, ws, CODE_HTTPERROR_402_NOTMODEST) |
102 | #define HTTPERROR_403_IP return http_issue_error( sock, ws, CODE_HTTPERROR_403_IP ) | 113 | #define HTTPERROR_403_IP return http_issue_error(sock, ws, CODE_HTTPERROR_403_IP) |
103 | #define HTTPERROR_404 return http_issue_error( sock, ws, CODE_HTTPERROR_404 ) | 114 | #define HTTPERROR_404 return http_issue_error(sock, ws, CODE_HTTPERROR_404) |
104 | #define HTTPERROR_500 return http_issue_error( sock, ws, CODE_HTTPERROR_500 ) | 115 | #define HTTPERROR_500 return http_issue_error(sock, ws, CODE_HTTPERROR_500) |
105 | ssize_t http_issue_error( const int64 sock, struct ot_workstruct *ws, int code ) { | 116 | ssize_t http_issue_error(const int64 sock, struct ot_workstruct *ws, int code) { |
106 | char *error_code[] = { "302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required", | 117 | char *error_code[] = {"302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required", |
107 | "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error" }; | 118 | "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error"}; |
108 | char *title = error_code[code]; | 119 | char *title = error_code[code]; |
109 | 120 | ||
110 | ws->reply = ws->outbuf; | 121 | ws->reply = ws->outbuf; |
111 | if( code == CODE_HTTPERROR_302 ) | 122 | if (code == CODE_HTTPERROR_302) |
112 | ws->reply_size = snprintf( ws->reply, G_OUTBUF_SIZE, "HTTP/1.0 302 Found\r\nContent-Length: 0\r\nLocation: %s\r\n\r\n", g_redirecturl ); | 123 | ws->reply_size = snprintf(ws->reply, G_OUTBUF_SIZE, "HTTP/1.0 302 Found\r\nContent-Length: 0\r\nLocation: %s\r\n\r\n", g_redirecturl); |
113 | else | 124 | else |
114 | ws->reply_size = snprintf( ws->reply, G_OUTBUF_SIZE, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", title, strlen(title)+16-4,title+4); | 125 | ws->reply_size = snprintf(ws->reply, G_OUTBUF_SIZE, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", title, |
126 | strlen(title) + 16 - 4, title + 4); | ||
115 | 127 | ||
116 | #ifdef _DEBUG_HTTPERROR | 128 | #ifdef _DEBUG_HTTPERROR |
117 | fprintf( stderr, "DEBUG: invalid request was: %s\n", ws->debugbuf ); | 129 | fprintf(stderr, "DEBUG: invalid request was: %s\n", ws->debugbuf); |
118 | #endif | 130 | #endif |
119 | stats_issue_event( EVENT_FAILED, FLAG_TCP, code ); | 131 | stats_issue_event(EVENT_FAILED, FLAG_TCP, code); |
120 | http_senddata( sock, ws ); | 132 | http_senddata(sock, ws); |
121 | return ws->reply_size = -2; | 133 | return ws->reply_size = -2; |
122 | } | 134 | } |
123 | 135 | ||
124 | ssize_t http_sendiovecdata( const int64 sock, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector ) { | 136 | ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector, int is_partial) { |
125 | struct http_data *cookie = io_getcookie( sock ); | 137 | struct http_data *cookie = io_getcookie(sock); |
126 | char *header; | 138 | io_batch *current; |
127 | int i; | 139 | char *header; |
128 | size_t header_size, size = iovec_length( &iovec_entries, (const struct iovec **)&iovector ); | 140 | const char *encoding = ""; |
129 | tai6464 t; | 141 | int i; |
142 | size_t header_size, size = iovec_length(&iovec_entries, (const struct iovec **)&iovector); | ||
143 | tai6464 t; | ||
130 | 144 | ||
131 | /* No cookie? Bad socket. Leave. */ | 145 | /* No cookie? Bad socket. Leave. */ |
132 | if( !cookie ) { | 146 | if (!cookie) { |
133 | iovec_free( &iovec_entries, &iovector ); | 147 | iovec_free(&iovec_entries, &iovector); |
134 | HTTPERROR_500; | 148 | HTTPERROR_500; |
135 | } | 149 | } |
136 | 150 | ||
137 | /* If this socket collected request in a buffer, free it now */ | 151 | /* If this socket collected request in a buffer, free it now */ |
138 | array_reset( &cookie->request ); | 152 | array_reset(&cookie->request); |
139 | 153 | ||
140 | /* If we came here, wait for the answer is over */ | 154 | /* If we came here, wait for the answer is over */ |
141 | cookie->flag &= ~STRUCT_HTTP_FLAG_WAITINGFORTASK; | 155 | if (cookie->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK) { |
142 | 156 | io_dontwantread(sock); | |
143 | /* Our answers never are 0 vectors. Return an error. */ | 157 | cookie->flag &= ~STRUCT_HTTP_FLAG_WAITINGFORTASK; |
144 | if( !iovec_entries ) { | ||
145 | HTTPERROR_500; | ||
146 | } | 158 | } |
147 | 159 | ||
148 | /* Prepare space for http header */ | 160 | if (iovec_entries) { |
149 | header = malloc( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING ); | 161 | |
150 | if( !header ) { | 162 | if (cookie->flag & STRUCT_HTTP_FLAG_ZSTD) |
151 | iovec_free( &iovec_entries, &iovector ); | 163 | encoding = "Content-Encoding: zstd\r\n"; |
152 | HTTPERROR_500; | 164 | else if (cookie->flag & STRUCT_HTTP_FLAG_GZIP) |
153 | } | 165 | encoding = "Content-Encoding: gzip\r\n"; |
154 | 166 | else if (cookie->flag & STRUCT_HTTP_FLAG_BZIP2) | |
155 | if( cookie->flag & STRUCT_HTTP_FLAG_GZIP ) | 167 | encoding = "Content-Encoding: bzip2\r\n"; |
156 | header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Encoding: gzip\r\nContent-Length: %zd\r\n\r\n", size ); | 168 | |
157 | else if( cookie->flag & STRUCT_HTTP_FLAG_BZIP2 ) | 169 | if (!(cookie->flag & STRUCT_HTTP_FLAG_CHUNKED)) |
158 | header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Encoding: bzip2\r\nContent-Length: %zd\r\n\r\n", size ); | 170 | header_size = asprintf(&header, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n%sContent-Length: %zd\r\n\r\n", encoding, size); |
159 | else | 171 | else { |
160 | header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r\n", size ); | 172 | if (!(cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER)) { |
173 | header_size = | ||
174 | asprintf(&header, "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\n%sTransfer-Encoding: chunked\r\n\r\n%zx\r\n", encoding, size); | ||
175 | cookie->flag |= STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER; | ||
176 | } else | ||
177 | header_size = asprintf(&header, "%zx\r\n", size); | ||
178 | } | ||
179 | if (!header) { | ||
180 | iovec_free(&iovec_entries, &iovector); | ||
181 | HTTPERROR_500; | ||
182 | } | ||
161 | 183 | ||
162 | if (!cookie->batch ) { | 184 | if (!cookie->batch) { |
163 | cookie->batch = malloc( sizeof(io_batch) ); | 185 | cookie->batch = malloc(sizeof(io_batch)); |
164 | memset( cookie->batch, 0, sizeof(io_batch) ); | 186 | if (!cookie->batch) { |
165 | cookie->batches = 1; | 187 | free(header); |
166 | } | 188 | iovec_free(&iovec_entries, &iovector); |
167 | iob_addbuf_free( cookie->batch, header, header_size ); | 189 | HTTPERROR_500; |
168 | 190 | } | |
169 | /* Split huge iovectors into separate io_batches */ | 191 | OT_IOB_INIT(cookie->batch); |
170 | for( i=0; i<iovec_entries; ++i ) { | 192 | cookie->batches = 1; |
171 | io_batch *current = cookie->batch + cookie->batches - 1; | 193 | } |
172 | 194 | current = cookie->batch + cookie->batches - 1; | |
173 | /* If the current batch's limit is reached, try to reallocate a new batch to work on */ | 195 | iob_addbuf_free(current, header, header_size); |
174 | if( current->bytesleft > OT_BATCH_LIMIT ) { | 196 | |
175 | io_batch * new_batch = realloc( current, (cookie->batches + 1) * sizeof(io_batch) ); | 197 | /* Split huge iovectors into separate io_batches */ |
176 | if( new_batch ) { | 198 | for (i = 0; i < iovec_entries; ++i) { |
177 | cookie->batches++; | 199 | /* If the current batch's limit is reached, try to reallocate a new batch to work on */ |
178 | current = cookie->batch = new_batch; | 200 | if (current->bytesleft > OT_BATCH_LIMIT) { |
179 | memset( current, 0, sizeof(io_batch) ); | 201 | io_batch *new_batch = realloc(cookie->batch, (cookie->batches + 1) * sizeof(io_batch)); |
202 | if (new_batch) { | ||
203 | cookie->batch = new_batch; | ||
204 | current = cookie->batch + cookie->batches++; | ||
205 | OT_IOB_INIT(current); | ||
180 | } | 206 | } |
207 | } | ||
208 | iob_addbuf_free(current, iovector[i].iov_base, iovector[i].iov_len); | ||
181 | } | 209 | } |
210 | free(iovector); | ||
211 | if (cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER) | ||
212 | iob_addbuf(current, "\r\n", 2); | ||
213 | } | ||
182 | 214 | ||
183 | iob_addbuf_free( current, iovector[i].iov_base, iovector[i].iov_len ); | 215 | if ((cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER) && cookie->batch && !is_partial) { |
216 | current = cookie->batch + cookie->batches - 1; | ||
217 | iob_addbuf(current, "0\r\n\r\n", 5); | ||
218 | cookie->flag &= ~STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER; | ||
184 | } | 219 | } |
185 | free( iovector ); | ||
186 | 220 | ||
187 | /* writeable sockets timeout after 10 minutes */ | 221 | /* writeable sockets timeout after 10 minutes */ |
188 | taia_now( &t ); taia_addsec( &t, &t, OT_CLIENT_TIMEOUT_SEND ); | 222 | taia_now(&t); |
189 | io_timeout( sock, t ); | 223 | taia_addsec(&t, &t, OT_CLIENT_TIMEOUT_SEND); |
190 | io_dontwantread( sock ); | 224 | io_timeout(sock, t); |
191 | io_wantwrite( sock ); | 225 | io_wantwrite(sock); |
192 | return 0; | 226 | return 0; |
193 | } | 227 | } |
194 | 228 | ||
195 | static ssize_t http_handle_stats( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) { | 229 | static ssize_t http_handle_stats(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { |
196 | static const ot_keywords keywords_main[] = | 230 | static const ot_keywords keywords_main[] = {{"mode", 1}, {"format", 2}, {"info_hash", 3}, {NULL, -3}}; |
197 | { { "mode", 1 }, {"format", 2 }, {"info_hash", 3}, { NULL, -3 } }; | 231 | static const ot_keywords keywords_mode[] = {{"peer", TASK_STATS_PEERS}, |
198 | static const ot_keywords keywords_mode[] = | 232 | {"conn", TASK_STATS_CONNS}, |
199 | { { "peer", TASK_STATS_PEERS }, { "conn", TASK_STATS_CONNS }, { "scrp", TASK_STATS_SCRAPE }, { "udp4", TASK_STATS_UDP }, { "tcp4", TASK_STATS_TCP }, | 233 | {"scrp", TASK_STATS_SCRAPE}, |
200 | { "busy", TASK_STATS_BUSY_NETWORKS }, { "torr", TASK_STATS_TORRENTS }, { "fscr", TASK_STATS_FULLSCRAPE }, | 234 | {"udp4", TASK_STATS_UDP}, |
201 | { "s24s", TASK_STATS_SLASH24S }, { "tpbs", TASK_STATS_TPB }, { "herr", TASK_STATS_HTTPERRORS }, { "completed", TASK_STATS_COMPLETED }, | 235 | {"tcp4", TASK_STATS_TCP}, |
202 | { "top100", TASK_STATS_TOP100 }, { "top10", TASK_STATS_TOP10 }, { "renew", TASK_STATS_RENEW }, { "syncs", TASK_STATS_SYNCS }, { "version", TASK_STATS_VERSION }, | 236 | {"busy", TASK_STATS_BUSY_NETWORKS}, |
203 | { "everything", TASK_STATS_EVERYTHING }, { "statedump", TASK_FULLSCRAPE_TRACKERSTATE }, { "fulllog", TASK_STATS_FULLLOG }, | 237 | {"torr", TASK_STATS_TORRENTS}, |
204 | { "woodpeckers", TASK_STATS_WOODPECKERS}, | 238 | {"fscr", TASK_STATS_FULLSCRAPE}, |
239 | {"s24s", TASK_STATS_SLASH24S}, | ||
240 | {"tpbs", TASK_STATS_TPB}, | ||
241 | {"herr", TASK_STATS_HTTPERRORS}, | ||
242 | {"completed", TASK_STATS_COMPLETED}, | ||
243 | {"top100", TASK_STATS_TOP100}, | ||
244 | {"top10", TASK_STATS_TOP10}, | ||
245 | {"renew", TASK_STATS_RENEW}, | ||
246 | {"syncs", TASK_STATS_SYNCS}, | ||
247 | {"version", TASK_STATS_VERSION}, | ||
248 | {"everything", TASK_STATS_EVERYTHING}, | ||
249 | {"statedump", TASK_FULLSCRAPE_TRACKERSTATE}, | ||
250 | {"fulllog", TASK_STATS_FULLLOG}, | ||
251 | {"woodpeckers", TASK_STATS_WOODPECKERS}, | ||
205 | #ifdef WANT_LOG_NUMWANT | 252 | #ifdef WANT_LOG_NUMWANT |
206 | { "numwants", TASK_STATS_NUMWANTS}, | 253 | {"numwants", TASK_STATS_NUMWANTS}, |
207 | #endif | 254 | #endif |
208 | { NULL, -3 } }; | 255 | {NULL, -3}}; |
209 | static const ot_keywords keywords_format[] = | 256 | static const ot_keywords keywords_format[] = {{"bin", TASK_FULLSCRAPE_TPB_BINARY}, {"ben", TASK_FULLSCRAPE}, |
210 | { { "bin", TASK_FULLSCRAPE_TPB_BINARY }, { "ben", TASK_FULLSCRAPE }, { "url", TASK_FULLSCRAPE_TPB_URLENCODED }, | 257 | {"url", TASK_FULLSCRAPE_TPB_URLENCODED}, {"txt", TASK_FULLSCRAPE_TPB_ASCII}, |
211 | { "txt", TASK_FULLSCRAPE_TPB_ASCII }, { "txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS }, { NULL, -3 } }; | 258 | {"txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS}, {NULL, -3}}; |
212 | 259 | ||
213 | int mode = TASK_STATS_PEERS, scanon = 1, format = 0; | 260 | int mode = TASK_STATS_PEERS, scanon = 1, format = 0; |
214 | 261 | ||
215 | #ifdef WANT_RESTRICT_STATS | 262 | #ifdef WANT_RESTRICT_STATS |
216 | struct http_data *cookie = io_getcookie( sock ); | 263 | struct http_data *cookie = io_getcookie(sock); |
217 | 264 | ||
218 | if( !cookie || !accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) | 265 | if (!cookie || !accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_STAT)) |
219 | HTTPERROR_403_IP; | 266 | HTTPERROR_403_IP; |
220 | #endif | 267 | #endif |
221 | 268 | ||
222 | while( scanon ) { | 269 | while (scanon) { |
223 | switch( scan_find_keywords( keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { | 270 | switch (scan_find_keywords(keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM)) { |
224 | case -2: scanon = 0; break; /* TERMINATOR */ | 271 | case -2: |
225 | case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ | 272 | scanon = 0; |
226 | case -3: scan_urlencoded_skipvalue( &read_ptr ); break; | 273 | break; /* TERMINATOR */ |
227 | case 1: /* matched "mode" */ | 274 | case -1: |
228 | if( ( mode = scan_find_keywords( keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; | 275 | HTTPERROR_400_PARAM; /* PARSE ERROR */ |
276 | case -3: | ||
277 | scan_urlencoded_skipvalue(&read_ptr); | ||
278 | break; | ||
279 | case 1: /* matched "mode" */ | ||
280 | if ((mode = scan_find_keywords(keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) | ||
281 | HTTPERROR_400_PARAM; | ||
229 | break; | 282 | break; |
230 | case 2: /* matched "format" */ | 283 | case 2: /* matched "format" */ |
231 | if( ( format = scan_find_keywords( keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; | 284 | if ((format = scan_find_keywords(keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) |
285 | HTTPERROR_400_PARAM; | ||
232 | break; | 286 | break; |
233 | case 3: HTTPERROR_400_PARAM; /* If the stats URL was mistakenly added as announce URL, return a 400 */ | 287 | case 3: |
288 | HTTPERROR_400_PARAM; /* If the stats URL was mistakenly added as announce URL, return a 400 */ | ||
234 | } | 289 | } |
235 | } | 290 | } |
236 | 291 | ||
237 | #ifdef WANT_FULLSCRAPE | 292 | #ifdef WANT_FULLSCRAPE |
238 | if( mode == TASK_FULLSCRAPE_TRACKERSTATE ) { | 293 | if (mode == TASK_FULLSCRAPE_TRACKERSTATE) { |
239 | format = mode; mode = TASK_STATS_TPB; | 294 | format = mode; |
295 | mode = TASK_STATS_TPB; | ||
240 | } | 296 | } |
241 | 297 | ||
242 | if( mode == TASK_STATS_TPB ) { | 298 | if (mode == TASK_STATS_TPB) { |
243 | struct http_data* cookie = io_getcookie( sock ); | 299 | struct http_data *cookie = io_getcookie(sock); |
244 | tai6464 t; | 300 | tai6464 t; |
245 | #ifdef WANT_COMPRESSION_GZIP | 301 | #ifdef WANT_COMPRESSION_GZIP |
246 | ws->request[ws->request_size] = 0; | 302 | ws->request[ws->request_size] = 0; |
247 | #ifdef WANT_COMPRESSION_GZIP_ALWAYS | 303 | #ifndef WANT_COMPRESSION_GZIP_ALWAYS |
248 | if( strstr( read_ptr - 1, "gzip" ) ) { | 304 | if (strstr(read_ptr - 1, "gzip")) { |
249 | #endif | 305 | #endif |
250 | cookie->flag |= STRUCT_HTTP_FLAG_GZIP; | 306 | cookie->flag |= STRUCT_HTTP_FLAG_GZIP; |
251 | format |= TASK_FLAG_GZIP; | 307 | format |= TASK_FLAG_GZIP; |
252 | #ifdef WANT_COMPRESSION_GZIP_ALWAYS | 308 | #ifndef WANT_COMPRESSION_GZIP_ALWAYS |
253 | } | 309 | } |
254 | #endif | 310 | #endif |
255 | #endif | 311 | #endif |
256 | /* Pass this task to the worker thread */ | 312 | /* Pass this task to the worker thread */ |
257 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; | 313 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED; |
258 | 314 | ||
259 | /* Clients waiting for us should not easily timeout */ | 315 | /* Clients waiting for us should not easily timeout */ |
260 | taia_uint( &t, 0 ); io_timeout( sock, t ); | 316 | taia_uint(&t, 0); |
261 | fullscrape_deliver( sock, format ); | 317 | io_timeout(sock, t); |
262 | io_dontwantread( sock ); | 318 | fullscrape_deliver(sock, format); |
319 | io_dontwantread(sock); | ||
263 | return ws->reply_size = -2; | 320 | return ws->reply_size = -2; |
264 | } | 321 | } |
265 | #endif | 322 | #endif |
266 | 323 | ||
267 | /* default format for now */ | 324 | /* default format for now */ |
268 | if( ( mode & TASK_CLASS_MASK ) == TASK_STATS ) { | 325 | if ((mode & TASK_CLASS_MASK) == TASK_STATS) { |
269 | tai6464 t; | 326 | tai6464 t; |
270 | /* Complex stats also include expensive memory debugging tools */ | 327 | /* Complex stats also include expensive memory debugging tools */ |
271 | taia_uint( &t, 0 ); io_timeout( sock, t ); | 328 | taia_uint(&t, 0); |
272 | stats_deliver( sock, mode ); | 329 | io_timeout(sock, t); |
330 | stats_deliver(sock, mode); | ||
273 | return ws->reply_size = -2; | 331 | return ws->reply_size = -2; |
274 | } | 332 | } |
275 | 333 | ||
276 | /* Simple stats can be answerred immediately */ | 334 | /* Simple stats can be answerred immediately */ |
277 | return ws->reply_size = return_stats_for_tracker( ws->reply, mode, 0 ); | 335 | return ws->reply_size = return_stats_for_tracker(ws->reply, mode, 0); |
278 | } | 336 | } |
279 | 337 | ||
280 | #ifdef WANT_MODEST_FULLSCRAPES | 338 | #ifdef WANT_MODEST_FULLSCRAPES |
281 | static pthread_mutex_t g_modest_fullscrape_mutex = PTHREAD_MUTEX_INITIALIZER; | 339 | static pthread_mutex_t g_modest_fullscrape_mutex = PTHREAD_MUTEX_INITIALIZER; |
282 | static ot_vector g_modest_fullscrape_timeouts; | 340 | static ot_vector g_modest_fullscrape_timeouts; |
283 | typedef struct { ot_ip6 ip; ot_time last_fullscrape; } ot_scrape_log; | 341 | typedef struct { |
342 | ot_ip6 ip; | ||
343 | ot_time last_fullscrape; | ||
344 | } ot_scrape_log; | ||
284 | #endif | 345 | #endif |
285 | 346 | ||
286 | #ifdef WANT_FULLSCRAPE | 347 | #ifdef WANT_FULLSCRAPE |
287 | static ssize_t http_handle_fullscrape( const int64 sock, struct ot_workstruct *ws ) { | 348 | static ssize_t http_handle_fullscrape(const int64 sock, struct ot_workstruct *ws) { |
288 | struct http_data* cookie = io_getcookie( sock ); | 349 | struct http_data *cookie = io_getcookie(sock); |
289 | int format = 0; | 350 | int format = 0; |
290 | tai6464 t; | 351 | tai6464 t; |
291 | 352 | ||
292 | #ifdef WANT_MODEST_FULLSCRAPES | 353 | #ifdef WANT_MODEST_FULLSCRAPES |
293 | { | 354 | { |
294 | ot_scrape_log this_peer, *new_peer; | 355 | ot_scrape_log this_peer, *new_peer; |
295 | int exactmatch; | 356 | int exactmatch; |
296 | memcpy( this_peer.ip, cookie->ip, sizeof(ot_ip6)); | 357 | memcpy(this_peer.ip, cookie->ip, sizeof(ot_ip6)); |
297 | this_peer.last_fullscrape = g_now_seconds; | 358 | this_peer.last_fullscrape = g_now_seconds; |
298 | pthread_mutex_lock(&g_modest_fullscrape_mutex); | 359 | pthread_mutex_lock(&g_modest_fullscrape_mutex); |
299 | new_peer = vector_find_or_insert( &g_modest_fullscrape_timeouts, &this_peer, sizeof(ot_scrape_log), sizeof(ot_ip6), &exactmatch ); | 360 | new_peer = vector_find_or_insert(&g_modest_fullscrape_timeouts, &this_peer, sizeof(ot_scrape_log), sizeof(ot_ip6), &exactmatch); |
300 | if( !new_peer ) { | 361 | if (!new_peer) { |
301 | pthread_mutex_unlock(&g_modest_fullscrape_mutex); | 362 | pthread_mutex_unlock(&g_modest_fullscrape_mutex); |
302 | HTTPERROR_500; | 363 | HTTPERROR_500; |
303 | } | 364 | } |
304 | if( exactmatch && ( this_peer.last_fullscrape - new_peer->last_fullscrape ) < OT_MODEST_PEER_TIMEOUT ) { | 365 | if (exactmatch && (this_peer.last_fullscrape - new_peer->last_fullscrape) < OT_MODEST_PEER_TIMEOUT) { |
305 | pthread_mutex_unlock(&g_modest_fullscrape_mutex); | 366 | pthread_mutex_unlock(&g_modest_fullscrape_mutex); |
306 | HTTPERROR_402_NOTMODEST; | 367 | HTTPERROR_402_NOTMODEST; |
307 | } | 368 | } |
308 | memcpy( new_peer, &this_peer, sizeof(ot_scrape_log)); | 369 | memcpy(new_peer, &this_peer, sizeof(ot_scrape_log)); |
309 | pthread_mutex_unlock(&g_modest_fullscrape_mutex); | 370 | pthread_mutex_unlock(&g_modest_fullscrape_mutex); |
310 | } | 371 | } |
311 | #endif | 372 | #endif |
312 | 373 | ||
374 | |||
375 | #if defined(WANT_COMPRESSION_GZIP) || defined(WANT_COMPRESSION_ZSTD) | ||
376 | ws->request[ws->request_size - 1] = 0; | ||
313 | #ifdef WANT_COMPRESSION_GZIP | 377 | #ifdef WANT_COMPRESSION_GZIP |
314 | ws->request[ws->request_size-1] = 0; | 378 | if (strstr(ws->request, "gzip")) { |
315 | if( strstr( ws->request, "gzip" ) ) { | ||
316 | cookie->flag |= STRUCT_HTTP_FLAG_GZIP; | 379 | cookie->flag |= STRUCT_HTTP_FLAG_GZIP; |
317 | format = TASK_FLAG_GZIP; | 380 | format |= TASK_FLAG_GZIP; |
318 | stats_issue_event( EVENT_FULLSCRAPE_REQUEST_GZIP, 0, (uintptr_t)cookie->ip ); | 381 | } |
319 | } else | 382 | #endif |
383 | #ifdef WANT_COMPRESSION_ZSTD | ||
384 | if (strstr(ws->request, "zstd")) { | ||
385 | cookie->flag |= STRUCT_HTTP_FLAG_ZSTD; | ||
386 | format |= TASK_FLAG_ZSTD; | ||
387 | } | ||
388 | #endif | ||
389 | |||
390 | #if defined(WANT_COMPRESSION_ZSTD) && defined(WANT_COMPRESSION_ZSTD_ALWAYS) | ||
391 | cookie->flag |= STRUCT_HTTP_FLAG_ZSTD; | ||
392 | format |= TASK_FLAG_ZSTD; | ||
320 | #endif | 393 | #endif |
321 | stats_issue_event( EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip ); | 394 | |
395 | #if defined(WANT_COMPRESSION_GZIP) && defined(WANT_COMPRESSION_GZIP_ALWAYS) | ||
396 | cookie->flag |= STRUCT_HTTP_FLAG_GZIP; | ||
397 | format |= TASK_FLAG_GZIP; | ||
398 | #endif | ||
399 | #endif | ||
400 | |||
401 | stats_issue_event(EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip); | ||
322 | 402 | ||
323 | #ifdef _DEBUG_HTTPERROR | 403 | #ifdef _DEBUG_HTTPERROR |
324 | fprintf( stderr, "%s", ws->debugbuf ); | 404 | fprintf(stderr, "%s", ws->debugbuf); |
325 | #endif | 405 | #endif |
326 | 406 | ||
327 | /* Pass this task to the worker thread */ | 407 | /* Pass this task to the worker thread */ |
328 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; | 408 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED; |
329 | /* Clients waiting for us should not easily timeout */ | 409 | /* Clients waiting for us should not easily timeout */ |
330 | taia_uint( &t, 0 ); io_timeout( sock, t ); | 410 | taia_uint(&t, 0); |
331 | fullscrape_deliver( sock, TASK_FULLSCRAPE | format ); | 411 | io_timeout(sock, t); |
332 | io_dontwantread( sock ); | 412 | fullscrape_deliver(sock, TASK_FULLSCRAPE | format); |
413 | io_dontwantread(sock); | ||
333 | return ws->reply_size = -2; | 414 | return ws->reply_size = -2; |
334 | } | 415 | } |
335 | #endif | 416 | #endif |
336 | 417 | ||
337 | static ssize_t http_handle_scrape( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) { | 418 | static ssize_t http_handle_scrape(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { |
338 | static const ot_keywords keywords_scrape[] = { { "info_hash", 1 }, { NULL, -3 } }; | 419 | static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}}; |
339 | 420 | ||
340 | ot_hash * multiscrape_buf = (ot_hash*)ws->request; | 421 | ot_hash *multiscrape_buf = (ot_hash *)ws->request; |
341 | int scanon = 1, numwant = 0; | 422 | int scanon = 1, numwant = 0; |
342 | 423 | ||
343 | /* This is to hack around stupid clients that send "scrape ?info_hash" */ | 424 | /* This is to hack around stupid clients that send "scrape ?info_hash" */ |
344 | if( read_ptr[-1] != '?' ) { | 425 | if (read_ptr[-1] != '?') { |
345 | while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr; | 426 | while ((*read_ptr != '?') && (*read_ptr != '\n')) |
346 | if( *read_ptr == '\n' ) HTTPERROR_400_PARAM; | 427 | ++read_ptr; |
428 | if (*read_ptr == '\n') | ||
429 | HTTPERROR_400_PARAM; | ||
347 | ++read_ptr; | 430 | ++read_ptr; |
348 | } | 431 | } |
349 | 432 | ||
350 | while( scanon ) { | 433 | while (scanon) { |
351 | switch( scan_find_keywords( keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { | 434 | switch (scan_find_keywords(keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM)) { |
352 | case -2: scanon = 0; break; /* TERMINATOR */ | 435 | case -2: |
353 | default: HTTPERROR_400_PARAM; /* PARSE ERROR */ | 436 | scanon = 0; |
354 | case -3: scan_urlencoded_skipvalue( &read_ptr ); break; | 437 | break; /* TERMINATOR */ |
355 | case 1: /* matched "info_hash" */ | 438 | default: |
439 | HTTPERROR_400_PARAM; /* PARSE ERROR */ | ||
440 | case -3: | ||
441 | scan_urlencoded_skipvalue(&read_ptr); | ||
442 | break; | ||
443 | case 1: /* matched "info_hash" */ | ||
356 | /* ignore this, when we have less than 20 bytes */ | 444 | /* ignore this, when we have less than 20 bytes */ |
357 | if( scan_urlencoded_query( &read_ptr, (char*)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE ) != (ssize_t)sizeof(ot_hash) ) | 445 | if (scan_urlencoded_query(&read_ptr, (char *)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE) != (ssize_t)sizeof(ot_hash)) |
358 | HTTPERROR_400_PARAM; | 446 | HTTPERROR_400_PARAM; |
359 | break; | 447 | break; |
360 | } | 448 | } |
361 | } | 449 | } |
362 | 450 | ||
363 | /* No info_hash found? Inform user */ | 451 | /* No info_hash found? Inform user */ |
364 | if( !numwant ) HTTPERROR_400_PARAM; | 452 | if (!numwant) |
453 | HTTPERROR_400_PARAM; | ||
365 | 454 | ||
366 | /* Limit number of hashes to process */ | 455 | /* Limit number of hashes to process */ |
367 | if( numwant > OT_MAXMULTISCRAPE_COUNT ) | 456 | if (numwant > OT_MAXMULTISCRAPE_COUNT) |
368 | numwant = OT_MAXMULTISCRAPE_COUNT; | 457 | numwant = OT_MAXMULTISCRAPE_COUNT; |
369 | 458 | ||
370 | /* Enough for http header + whole scrape string */ | 459 | /* Enough for http header + whole scrape string */ |
371 | ws->reply_size = return_tcp_scrape_for_torrent( multiscrape_buf, numwant, ws->reply ); | 460 | ws->reply_size = return_tcp_scrape_for_torrent((const ot_hash *)multiscrape_buf, numwant, ws->reply); |
372 | stats_issue_event( EVENT_SCRAPE, FLAG_TCP, ws->reply_size ); | 461 | stats_issue_event(EVENT_SCRAPE, FLAG_TCP, ws->reply_size); |
373 | return ws->reply_size; | 462 | return ws->reply_size; |
374 | } | 463 | } |
375 | 464 | ||
376 | #ifdef WANT_LOG_NUMWANT | 465 | #ifdef WANT_LOG_NUMWANT |
377 | unsigned long long numwants[201]; | 466 | unsigned long long numwants[201]; |
378 | #endif | 467 | #endif |
379 | 468 | ||
380 | #if defined( WANT_KEEPALIVE ) || defined( WANT_IP_FROM_PROXY ) | 469 | #if defined(WANT_KEEPALIVE) || defined(WANT_IP_FROM_PROXY) |
381 | static char* http_header( char *data, size_t byte_count, char *header ) { | 470 | static char *http_header(char *data, size_t byte_count, char *header) { |
382 | size_t i; | 471 | size_t i; |
383 | long sl = strlen( header ); | 472 | long sl = strlen(header); |
384 | for( i = 0; i + sl + 2 < byte_count; ++i ) { | 473 | for (i = 0; i + sl + 2 < byte_count; ++i) { |
385 | if( data[i] != '\n' || data[ i + sl + 1] != ':' ) continue; | 474 | if (data[i] != '\n' || data[i + sl + 1] != ':') |
386 | if( !case_equalb( data + i + 1, sl, header ) ) continue; | 475 | continue; |
476 | if (!case_equalb(data + i + 1, sl, header)) | ||
477 | continue; | ||
387 | data += i + sl + 2; | 478 | data += i + sl + 2; |
388 | while( *data == ' ' || *data == '\t' ) ++data; | 479 | while (*data == ' ' || *data == '\t') |
480 | ++data; | ||
389 | return data; | 481 | return data; |
390 | } | 482 | } |
391 | return 0; | 483 | return 0; |
392 | } | 484 | } |
393 | #endif | 485 | #endif |
394 | 486 | ||
395 | static ot_keywords keywords_announce[] = { { "port", 1 }, { "left", 2 }, { "event", 3 }, { "numwant", 4 }, { "compact", 5 }, { "compact6", 5 }, { "info_hash", 6 }, | 487 | static ot_keywords keywords_announce[] = {{"port", 1}, {"left", 2}, {"event", 3}, {"numwant", 4}, {"compact", 5}, {"compact6", 5}, {"info_hash", 6}, |
396 | #ifdef WANT_IP_FROM_QUERY_STRING | 488 | #ifdef WANT_IP_FROM_QUERY_STRING |
397 | { "ip", 7 }, | 489 | {"ip", 7}, |
398 | #endif | 490 | #endif |
399 | #ifdef WANT_FULLLOG_NETWORKS | 491 | #ifdef WANT_FULLLOG_NETWORKS |
400 | { "lognet", 8 }, | 492 | {"lognet", 8}, |
401 | #endif | 493 | #endif |
402 | { "peer_id", 9 }, | 494 | {"peer_id", 9}, {NULL, -3}}; |
403 | { NULL, -3 } }; | 495 | static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}}; |
404 | static ot_keywords keywords_announce_event[] = { { "completed", 1 }, { "stopped", 2 }, { NULL, -3 } }; | 496 | static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { |
405 | static ssize_t http_handle_announce( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) { | ||
406 | int numwant, tmp, scanon; | 497 | int numwant, tmp, scanon; |
407 | unsigned short port = 0; | 498 | unsigned short port = 0; |
408 | char *write_ptr; | 499 | char *write_ptr; |
409 | ssize_t len; | 500 | ssize_t len; |
410 | struct http_data *cookie = io_getcookie( sock ); | 501 | struct http_data *cookie = io_getcookie(sock); |
411 | 502 | ||
412 | /* This is to hack around stupid clients that send "announce ?info_hash" */ | 503 | /* This is to hack around stupid clients that send "announce ?info_hash" */ |
413 | if( read_ptr[-1] != '?' ) { | 504 | if (read_ptr[-1] != '?') { |
414 | while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr; | 505 | while ((*read_ptr != '?') && (*read_ptr != '\n')) |
415 | if( *read_ptr == '\n' ) HTTPERROR_400_PARAM; | 506 | ++read_ptr; |
507 | if (*read_ptr == '\n') | ||
508 | HTTPERROR_400_PARAM; | ||
416 | ++read_ptr; | 509 | ++read_ptr; |
417 | } | 510 | } |
418 | 511 | ||
419 | #ifdef WANT_IP_FROM_PROXY | 512 | #ifdef WANT_IP_FROM_PROXY |
420 | if( accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_PROXY ) ) { | 513 | if (accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_PROXY)) { |
421 | ot_ip6 proxied_ip; | 514 | ot_ip6 proxied_ip; |
422 | char *fwd = http_header( ws->request, ws->header_size, "x-forwarded-for" ); | 515 | char *fwd = http_header(ws->request, ws->header_size, "x-forwarded-for"); |
423 | if( fwd && scan_ip6( fwd, proxied_ip ) ) | 516 | if (fwd && scan_ip6(fwd, proxied_ip)) { |
424 | OT_SETIP( &ws->peer, proxied_ip ); | 517 | OT_SETIP(ws->peer, proxied_ip); |
425 | else | 518 | } else |
426 | OT_SETIP( &ws->peer, cookie->ip ); | 519 | OT_SETIP(ws->peer, cookie->ip); |
427 | } else | 520 | } else |
428 | #endif | 521 | #endif |
429 | OT_SETIP( &ws->peer, cookie->ip ); | 522 | OT_SETIP(ws->peer, cookie->ip); |
430 | 523 | ||
431 | ws->peer_id = NULL; | 524 | ws->peer_id = NULL; |
432 | ws->hash = NULL; | 525 | ws->hash = NULL; |
433 | 526 | ||
434 | OT_SETPORT( &ws->peer, &port ); | 527 | OT_SETPORT(ws->peer, &port); |
435 | OT_PEERFLAG( &ws->peer ) = 0; | 528 | OT_PEERFLAG(ws->peer) = 0; |
436 | numwant = 50; | 529 | numwant = 50; |
437 | scanon = 1; | 530 | scanon = 1; |
438 | 531 | ||
439 | while( scanon ) { | 532 | while (scanon) { |
440 | switch( scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { | 533 | switch (scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM)) { |
441 | case -2: scanon = 0; break; /* TERMINATOR */ | 534 | case -2: |
442 | case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ | 535 | scanon = 0; |
443 | case -3: scan_urlencoded_skipvalue( &read_ptr ); break; | 536 | break; /* TERMINATOR */ |
537 | case -1: | ||
538 | HTTPERROR_400_PARAM; /* PARSE ERROR */ | ||
539 | case -3: | ||
540 | scan_urlencoded_skipvalue(&read_ptr); | ||
541 | break; | ||
444 | case 1: /* matched "port" */ | 542 | case 1: /* matched "port" */ |
445 | len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); | 543 | len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); |
446 | if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM; | 544 | if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp) || (tmp > 0xffff)) |
447 | port = htons( tmp ); OT_SETPORT( &ws->peer, &port ); | 545 | HTTPERROR_400_PARAM; |
546 | port = htons(tmp); | ||
547 | OT_SETPORT(&ws->peer, &port); | ||
448 | break; | 548 | break; |
449 | case 2: /* matched "left" */ | 549 | case 2: /* matched "left" */ |
450 | if( ( len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; | 550 | if ((len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) |
451 | if( scan_fixed_int( write_ptr, len, &tmp ) ) tmp = 0; | 551 | HTTPERROR_400_PARAM; |
452 | if( !tmp ) OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_SEEDING; | 552 | if (scan_fixed_int(write_ptr, len, &tmp)) |
553 | tmp = 0; | ||
554 | if (!tmp) | ||
555 | OT_PEERFLAG(&ws->peer) |= PEER_FLAG_SEEDING; | ||
453 | break; | 556 | break; |
454 | case 3: /* matched "event" */ | 557 | case 3: /* matched "event" */ |
455 | switch( scan_find_keywords( keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) { | 558 | switch (scan_find_keywords(keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE)) { |
456 | case -1: HTTPERROR_400_PARAM; | 559 | case -1: |
457 | case 1: /* matched "completed" */ | 560 | HTTPERROR_400_PARAM; |
458 | OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_COMPLETED; | 561 | case 1: /* matched "completed" */ |
459 | break; | 562 | OT_PEERFLAG(&ws->peer) |= PEER_FLAG_COMPLETED; |
460 | case 2: /* matched "stopped" */ | 563 | break; |
461 | OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_STOPPED; | 564 | case 2: /* matched "stopped" */ |
462 | break; | 565 | OT_PEERFLAG(&ws->peer) |= PEER_FLAG_STOPPED; |
463 | default: | 566 | break; |
464 | break; | 567 | default: |
568 | break; | ||
465 | } | 569 | } |
466 | break; | 570 | break; |
467 | case 4: /* matched "numwant" */ | 571 | case 4: /* matched "numwant" */ |
468 | len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); | 572 | len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); |
469 | if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &numwant ) ) HTTPERROR_400_PARAM; | 573 | if ((len <= 0) || scan_fixed_int(write_ptr, len, &numwant)) |
470 | if( numwant < 0 ) numwant = 50; | 574 | HTTPERROR_400_PARAM; |
471 | if( numwant > 200 ) numwant = 200; | 575 | if (numwant < 0) |
576 | numwant = 50; | ||
577 | if (numwant > 200) | ||
578 | numwant = 200; | ||
472 | break; | 579 | break; |
473 | case 5: /* matched "compact" */ | 580 | case 5: /* matched "compact" */ |
474 | len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); | 581 | len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); |
475 | if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) ) HTTPERROR_400_PARAM; | 582 | if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp)) |
476 | if( !tmp ) HTTPERROR_400_COMPACT; | 583 | HTTPERROR_400_PARAM; |
584 | if (!tmp) | ||
585 | HTTPERROR_400_COMPACT; | ||
477 | break; | 586 | break; |
478 | case 6: /* matched "info_hash" */ | 587 | case 6: /* matched "info_hash" */ |
479 | if( ws->hash ) HTTPERROR_400_DOUBLEHASH; | 588 | if (ws->hash) |
589 | HTTPERROR_400_DOUBLEHASH; | ||
480 | /* ignore this, when we have less than 20 bytes */ | 590 | /* ignore this, when we have less than 20 bytes */ |
481 | if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; | 591 | if (scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) |
482 | ws->hash = (ot_hash*)write_ptr; | 592 | HTTPERROR_400_PARAM; |
593 | ws->hash = (ot_hash *)write_ptr; | ||
483 | break; | 594 | break; |
484 | #ifdef WANT_IP_FROM_QUERY_STRING | 595 | #ifdef WANT_IP_FROM_QUERY_STRING |
485 | case 7: /* matched "ip" */ | 596 | case 7: /* matched "ip" */ |
486 | { | 597 | { |
487 | char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply+16; | 598 | char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16; |
488 | len = scan_urlencoded_query( &read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE ); | 599 | len = scan_urlencoded_query(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE); |
489 | tmp_buf2[len] = 0; | 600 | tmp_buf2[len] = 0; |
490 | if( ( len <= 0 ) || !scan_ip6( tmp_buf2, tmp_buf1 ) ) HTTPERROR_400_PARAM; | 601 | if ((len <= 0) || !scan_ip6(tmp_buf2, tmp_buf1)) |
491 | OT_SETIP( &ws->peer, tmp_buf1 ); | 602 | HTTPERROR_400_PARAM; |
492 | } | 603 | OT_SETIP(&ws->peer, tmp_buf1); |
493 | break; | 604 | } break; |
494 | #endif | 605 | #endif |
495 | #ifdef WANT_FULLLOG_NETWORKS | 606 | #ifdef WANT_FULLLOG_NETWORKS |
496 | case 8: /* matched "lognet" */ | 607 | case 8: /* matched "lognet" */ |
497 | { | 608 | { |
498 | //if( accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) { | 609 | // if( accesslist_is_blessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) { |
499 | char *tmp_buf = ws->reply; | 610 | char *tmp_buf = ws->reply; |
500 | ot_net net; | 611 | ot_net net; |
501 | signed short parsed, bits; | 612 | signed short parsed, bits; |
502 | 613 | ||
503 | len = scan_urlencoded_query( &read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE ); | 614 | len = scan_urlencoded_query(&read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE); |
504 | tmp_buf[len] = 0; | 615 | tmp_buf[len] = 0; |
505 | if( len <= 0 ) HTTPERROR_400_PARAM; | 616 | if (len <= 0) |
506 | if( *tmp_buf == '-' ) { | 617 | HTTPERROR_400_PARAM; |
507 | loglist_reset( ); | 618 | if (*tmp_buf == '-') { |
508 | return ws->reply_size = sprintf( ws->reply, "Successfully removed.\n" ); | 619 | loglist_reset(); |
509 | } | 620 | return ws->reply_size = sprintf(ws->reply, "Successfully removed.\n"); |
510 | parsed = scan_ip6( tmp_buf, net.address ); | ||
511 | if( !parsed ) HTTPERROR_400_PARAM; | ||
512 | if( tmp_buf[parsed++] != '/' ) | ||
513 | bits = 128; | ||
514 | else { | ||
515 | parsed = scan_short( tmp_buf + parsed, &bits ); | ||
516 | if( !parsed ) HTTPERROR_400_PARAM; | ||
517 | if( ip6_isv4mapped( net.address ) ) | ||
518 | bits += 96; | ||
519 | } | ||
520 | net.bits = bits; | ||
521 | loglist_add_network( &net ); | ||
522 | return ws->reply_size = sprintf( ws->reply, "Successfully added.\n" ); | ||
523 | //} | ||
524 | } | 621 | } |
525 | break; | 622 | parsed = scan_ip6(tmp_buf, net.address); |
623 | if (!parsed) | ||
624 | HTTPERROR_400_PARAM; | ||
625 | if (tmp_buf[parsed++] != '/') | ||
626 | bits = 128; | ||
627 | else { | ||
628 | parsed = scan_short(tmp_buf + parsed, &bits); | ||
629 | if (!parsed) | ||
630 | HTTPERROR_400_PARAM; | ||
631 | if (ip6_isv4mapped(net.address)) | ||
632 | bits += 96; | ||
633 | } | ||
634 | net.bits = bits; | ||
635 | loglist_add_network(&net); | ||
636 | return ws->reply_size = sprintf(ws->reply, "Successfully added.\n"); | ||
637 | //} | ||
638 | } break; | ||
526 | #endif | 639 | #endif |
527 | case 9: /* matched "peer_id" */ | 640 | case 9: /* matched "peer_id" */ |
528 | /* ignore this, when we have less than 20 bytes */ | 641 | /* ignore this, when we have less than 20 bytes */ |
529 | if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; | 642 | if (scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) |
530 | ws->peer_id = write_ptr; | 643 | HTTPERROR_400_PARAM; |
531 | break; | 644 | ws->peer_id = write_ptr; |
645 | break; | ||
532 | } | 646 | } |
533 | } | 647 | } |
534 | 648 | ||
@@ -541,100 +655,107 @@ static ssize_t http_handle_announce( const int64 sock, struct ot_workstruct *ws, | |||
541 | */ | 655 | */ |
542 | 656 | ||
543 | /* Scanned whole query string */ | 657 | /* Scanned whole query string */ |
544 | if( !ws->hash ) | 658 | if (!ws->hash) |
545 | return ws->reply_size = sprintf( ws->reply, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e" ); | 659 | return ws->reply_size = sprintf(ws->reply, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e"); |
546 | 660 | ||
547 | if( OT_PEERFLAG( &ws->peer ) & PEER_FLAG_STOPPED ) | 661 | if (OT_PEERFLAG(&ws->peer) & PEER_FLAG_STOPPED) |
548 | ws->reply_size = remove_peer_from_torrent( FLAG_TCP, ws ); | 662 | ws->reply_size = remove_peer_from_torrent(FLAG_TCP, ws); |
549 | else | 663 | else |
550 | ws->reply_size = add_peer_to_torrent_and_return_peers( FLAG_TCP, ws, numwant ); | 664 | ws->reply_size = add_peer_to_torrent_and_return_peers(FLAG_TCP, ws, numwant); |
551 | 665 | ||
552 | stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size); | 666 | stats_issue_event(EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size); |
553 | return ws->reply_size; | 667 | return ws->reply_size; |
554 | } | 668 | } |
555 | 669 | ||
556 | ssize_t http_handle_request( const int64 sock, struct ot_workstruct *ws ) { | 670 | ssize_t http_handle_request(const int64 sock, struct ot_workstruct *ws) { |
557 | ssize_t reply_off, len; | 671 | ssize_t reply_off, len; |
558 | char *read_ptr = ws->request, *write_ptr; | 672 | char *read_ptr = ws->request, *write_ptr; |
559 | 673 | ||
560 | #ifdef WANT_FULLLOG_NETWORKS | 674 | #ifdef WANT_FULLLOG_NETWORKS |
561 | struct http_data *cookie = io_getcookie( sock ); | 675 | struct http_data *cookie = io_getcookie(sock); |
562 | if( loglist_check_address( cookie->ip ) ) { | 676 | if (loglist_check_address(cookie->ip)) { |
563 | ot_log *log = malloc( sizeof( ot_log ) ); | 677 | ot_log *log = malloc(sizeof(ot_log)); |
564 | if( log ) { | 678 | if (log) { |
565 | log->size = ws->request_size; | 679 | log->size = ws->request_size; |
566 | log->data = malloc( ws->request_size ); | 680 | log->data = malloc(ws->request_size); |
567 | log->next = 0; | 681 | log->next = 0; |
568 | log->time = g_now_seconds; | 682 | log->time = g_now_seconds; |
569 | memcpy( log->ip, cookie->ip, sizeof(ot_ip6)); | 683 | memcpy(log->ip, cookie->ip, sizeof(ot_ip6)); |
570 | if( log->data ) { | 684 | if (log->data) { |
571 | memcpy( log->data, ws->request, ws->request_size ); | 685 | memcpy(log->data, ws->request, ws->request_size); |
572 | if( !g_logchain_first ) | 686 | if (!g_logchain_first) |
573 | g_logchain_first = g_logchain_last = log; | 687 | g_logchain_first = g_logchain_last = log; |
574 | else { | 688 | else { |
575 | g_logchain_last->next = log; | 689 | g_logchain_last->next = log; |
576 | g_logchain_last = log; | 690 | g_logchain_last = log; |
577 | } | 691 | } |
578 | } else | 692 | } else |
579 | free( log ); | 693 | free(log); |
580 | } | 694 | } |
581 | } | 695 | } |
582 | #endif | 696 | #endif |
583 | 697 | ||
584 | #ifdef _DEBUG_HTTPERROR | 698 | #ifdef _DEBUG_HTTPERROR |
585 | reply_off = ws->request_size; | 699 | reply_off = ws->request_size; |
586 | if( ws->request_size >= G_DEBUGBUF_SIZE ) | 700 | if (ws->request_size >= G_DEBUGBUF_SIZE) |
587 | reply_off = G_DEBUGBUF_SIZE - 1; | 701 | reply_off = G_DEBUGBUF_SIZE - 1; |
588 | memcpy( ws->debugbuf, ws->request, reply_off ); | 702 | memcpy(ws->debugbuf, ws->request, reply_off); |
589 | ws->debugbuf[ reply_off ] = 0; | 703 | ws->debugbuf[reply_off] = 0; |
590 | #endif | 704 | #endif |
591 | 705 | ||
592 | /* Tell subroutines where to put reply data */ | 706 | /* Tell subroutines where to put reply data */ |
593 | ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH; | 707 | ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH; |
594 | 708 | ||
595 | /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */ | 709 | /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */ |
596 | if( memcmp( read_ptr, "GET /", 5) ) HTTPERROR_400; | 710 | if (memcmp(read_ptr, "GET /", 5)) |
711 | HTTPERROR_400; | ||
597 | 712 | ||
598 | /* Skip leading '/' */ | 713 | /* Skip leading '/' */ |
599 | for( read_ptr+=4; *read_ptr == '/'; ++read_ptr); | 714 | for (read_ptr += 4; *read_ptr == '/'; ++read_ptr) |
715 | ; | ||
600 | 716 | ||
601 | /* Try to parse the request. | 717 | /* Try to parse the request. |
602 | In reality we abandoned requiring the url to be correct. This now | 718 | In reality we abandoned requiring the url to be correct. This now |
603 | only decodes url encoded characters, we check for announces and | 719 | only decodes url encoded characters, we check for announces and |
604 | scrapes by looking for "a*" or "sc" */ | 720 | scrapes by looking for "a*" or "sc" */ |
605 | len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_PATH ); | 721 | len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_PATH); |
606 | 722 | ||
607 | /* If parsing returned an error, leave with not found */ | 723 | /* If parsing returned an error, leave with not found */ |
608 | if( g_redirecturl && ( len == -2 ) ) HTTPERROR_302; | 724 | if (g_redirecturl && (len == -2)) |
609 | if( len <= 0 ) HTTPERROR_404; | 725 | HTTPERROR_302; |
726 | if (len <= 0) | ||
727 | HTTPERROR_404; | ||
610 | 728 | ||
611 | /* This is the hardcore match for announce*/ | 729 | /* This is the hardcore match for announce*/ |
612 | if( ( *write_ptr == 'a' ) || ( *write_ptr == '?' ) ) | 730 | if ((*write_ptr == 'a') || (*write_ptr == '?')) |
613 | http_handle_announce( sock, ws, read_ptr ); | 731 | http_handle_announce(sock, ws, read_ptr); |
614 | #ifdef WANT_FULLSCRAPE | 732 | #ifdef WANT_FULLSCRAPE |
615 | else if( !memcmp( write_ptr, "scrape HTTP/", 12 ) ) | 733 | else if (!memcmp(write_ptr, "scrape HTTP/", 12)) |
616 | http_handle_fullscrape( sock, ws ); | 734 | http_handle_fullscrape(sock, ws); |
617 | #endif | 735 | #endif |
618 | /* This is the hardcore match for scrape */ | 736 | /* This is the hardcore match for scrape */ |
619 | else if( !memcmp( write_ptr, "sc", 2 ) ) | 737 | else if (!memcmp(write_ptr, "sc", 2)) |
620 | http_handle_scrape( sock, ws, read_ptr ); | 738 | http_handle_scrape(sock, ws, read_ptr); |
621 | /* All the rest is matched the standard way */ | 739 | /* All the rest is matched the standard way */ |
622 | else if( len == g_stats_path_len && !memcmp( write_ptr, g_stats_path, len ) ) | 740 | else if (len == g_stats_path_len && !memcmp(write_ptr, g_stats_path, len)) |
623 | http_handle_stats( sock, ws, read_ptr ); | 741 | http_handle_stats(sock, ws, read_ptr); |
624 | else | 742 | else |
625 | HTTPERROR_404; | 743 | HTTPERROR_404; |
626 | 744 | ||
627 | /* Find out if the client wants to keep this connection alive */ | 745 | /* Find out if the client wants to keep this connection alive */ |
628 | ws->keep_alive = 0; | 746 | ws->keep_alive = 0; |
629 | #ifdef WANT_KEEPALIVE | 747 | #ifdef WANT_KEEPALIVE |
630 | read_ptr=http_header( ws->request, ws->header_size, "connection"); | 748 | read_ptr = http_header(ws->request, ws->header_size, "connection"); |
631 | if( read_ptr && ( *read_ptr == 'K' || *read_ptr == 'k' ) ) ws->keep_alive = 1; | 749 | if (read_ptr && (*read_ptr == 'K' || *read_ptr == 'k')) |
750 | ws->keep_alive = 1; | ||
632 | #endif | 751 | #endif |
633 | 752 | ||
634 | /* If routines handled sending themselves, just return */ | 753 | /* If routines handled sending themselves, just return */ |
635 | if( ws->reply_size == -2 ) return 0; | 754 | if (ws->reply_size == -2) |
755 | return 0; | ||
636 | /* If routine failed, let http error take over */ | 756 | /* If routine failed, let http error take over */ |
637 | if( ws->reply_size <= 0 ) HTTPERROR_500; | 757 | if (ws->reply_size <= 0) |
758 | HTTPERROR_500; | ||
638 | 759 | ||
639 | /* This one is rather ugly, so I take you step by step through it. | 760 | /* This one is rather ugly, so I take you step by step through it. |
640 | 761 | ||
@@ -643,18 +764,16 @@ ssize_t http_handle_request( const int64 sock, struct ot_workstruct *ws ) { | |||
643 | plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate | 764 | plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate |
644 | the space NOT needed to expand in reply_off | 765 | the space NOT needed to expand in reply_off |
645 | */ | 766 | */ |
646 | reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( ws->outbuf, 0, "%zd", ws->reply_size ); | 767 | reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf(ws->outbuf, 0, "%zd", ws->reply_size); |
647 | ws->reply = ws->outbuf + reply_off; | 768 | ws->reply = ws->outbuf + reply_off; |
648 | 769 | ||
649 | /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete | 770 | /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete |
650 | packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ | 771 | packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ |
651 | ws->reply_size += 1 + sprintf( ws->reply, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", ws->reply_size ); | 772 | ws->reply_size += 1 + sprintf(ws->reply, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", ws->reply_size); |
652 | 773 | ||
653 | /* 3. Finally we join both blocks neatly */ | 774 | /* 3. Finally we join both blocks neatly */ |
654 | ws->outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; | 775 | ws->outbuf[SUCCESS_HTTP_HEADER_LENGTH - 1] = '\n'; |
655 | 776 | ||
656 | http_senddata( sock, ws ); | 777 | http_senddata(sock, ws); |
657 | return ws->reply_size; | 778 | return ws->reply_size; |
658 | } | 779 | } |
659 | |||
660 | const char *g_version_http_c = "$Source$: $Revision$\n"; | ||