OSDN Git Service

Deleted HTTP header modifires
[ultramonkey-l7/ultramonkey-l7-v2.git] / module / protocol / module_http.c
1 /*
2  * @file  module_http.c
3  * @brief Library of HTTP protocol for protocol module.
4  * @brief this library provide HTTP header control.
5  *
6  * L7VSD: Linux Virtual Server for Layer7 Load Balancing
7  * Copyright (C) 2008  NTT COMWARE Corporation.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22  * 02110-1301 USA
23  *
24  **********************************************************************/
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <ctype.h>
30 #include <unistd.h>
31 #include <time.h>
32 #include "module_http.h"
33
34
35 /*!
36  * check the HTTP status that is included in a HTTP Response header.
37  * (RFC2616 http://www.ietf.org/rfc/rfc2616.txt)
38  * @param  *res HTTP Response header
39  * @retval 0    HTTP status is 2xx or 3xx, or 1xx.
40  * @retval -1   HTTP status is 4xx or 5xx, or is not able to be found.
41  */
42 extern int
43 http_check_response_status(char *res)
44 {
45         /* check NULL */
46         if (res == NULL) {
47                 return -1;
48         }
49
50         /* pattern match "HTTP/X.Y ZYY " (X: 0 or 1) (Y: 0 ~ 9) (Z: 1 ~ 3) */
51         if ( res[0] != 'H' || res[1] != 'T' || res[2] != 'T' || res[3] != 'P' ||
52              res[4] != '/' || ( res[5] != '1' && res[5] != '0' ) ||
53              res[6] != '.' || !isdigit(res[7]) || res[8] != ' ' ||
54              ( res[9] != '2' && res[9] != '3' && res[9] != '1' ) ||
55              !isdigit(res[10]) || !isdigit(res[11]) || res[12] != ' ' ) {
56                 return -1;
57         }
58
59         return 0;
60 }
61
62 /*!
63  * check the HTTP method that is included in a HTTP Request header.
64  * (RFC2616 http://www.ietf.org/rfc/rfc2616.txt)
65  * @param  *req HTTP Request header
66  * @return points to request-URI when HTTP method is valid.
67  *         (It means that method is one of below.
68  *         GET, HEAD, POST, PUT, DELETE, OPTIONS, TRACE, CONNECT,
69  *         PROPFIND, PROPPATCH, COPY, MOVE, MKCOL, LOCK, UNLOCK)
70  *         NULL when HTTP method is not valid.
71  */
72 extern char *
73 http_check_request_method(char *req, size_t *length)
74 {
75         char *uri = NULL;
76         char *ptr = NULL;
77         char *S = NULL;
78         char *check_len =" HTTP/1." ;
79         size_t len_count;
80         size_t len = *length;
81         int ret;
82
83         /* check NULL */
84         if (req == NULL) {
85                 return NULL;
86         }
87
88         /* check length size */
89         if (len < 16)
90         {
91                 return NULL;
92         }
93
94         /* request check */
95         switch(req[0])
96         {
97         case 'G':
98                 /* GET method */
99                 if (req[1] != 'E' || req[2] != 'T' || req[3] != ' ') {
100                         return NULL;
101                 }
102                 uri = req + 4;
103                 break;
104                 
105         case 'H':
106                 /* HEAD method */
107                 if (req[1] != 'E' || req[2] != 'A' || req[3] != 'D' || req[4] != ' ') {
108                         return NULL;
109                 }
110                 uri = req + 5;
111                 break;
112                 
113         case 'P':
114                 switch(req[1])
115                 {
116                 case 'O':
117                         /* POST method */
118                         if (req[2] != 'S' || req[3] != 'T' || req[4] != ' ') {
119                                 return NULL;
120                         }
121                         uri = req + 5;
122                         break;
123                         
124                 case 'U':
125                         /* PUT method */
126                         if (req[2] != 'T' || req[3] != ' ') {
127                                 return NULL;
128                         }
129                         uri = req + 4;
130                         break;
131                         
132                 case 'R':
133                         if (req[2] != 'O' || req[3] != 'P') {
134                                 return NULL;
135                         }
136                         switch(req[4])
137                         {
138                         case 'F':
139                                 /* PROPFIND method (WebDAV) */
140                                 if (req[5] != 'I' || req[6] != 'N' || req[7] != 'D' ||
141                                     req[8] != ' ') {
142                                         return NULL;
143                                 }
144                                 uri = req + 9;
145                                 break;
146                                 
147                         case 'P':
148                                 /* PROPPATCH method (WebDAV) */
149                                 if (req[5] != 'A' || req[6] != 'T' || req[7] != 'C' || req[8] != 'H' ||
150                                     req[9] != ' ') {
151                                         return NULL;
152                                 }
153                                 uri = req + 10;
154                                 break;
155                                 
156                         default:
157                                 /* other PROP method */
158                                 return NULL;
159                         }
160                         break;
161                         
162                 default:
163                         /* other P method */
164                         return NULL;
165                 }
166                 break;
167                 
168         case 'O':
169                 /* OPTIONS method */
170                 if (req[1] != 'P' || req[2] != 'T' || req[3] != 'I' || req[4] != 'O' ||
171                     req[5] != 'N' || req[6] != 'S' || req[7] != ' ') {
172                         return NULL;
173                 }
174                 uri = req + 8;
175                 break;
176                 
177         case 'C':
178                 if (req[1] != 'O') {
179                         /* other C method */
180                         return NULL;
181                 }
182                 switch(req[2])
183                 {
184                 case 'N':
185                         /* CONNECT method */
186                         if (req[3] != 'N' || req[4] != 'E' || req[5] != 'C' || req[6] != 'T' ||
187                             req[7] != ' ') {
188                                 return NULL;
189                         }
190                         uri = req + 8;
191                         break;
192                         
193                 case 'P':
194                         /* COPY method (WebDAV) */
195                         if (req[3] != 'Y' || req[4] != ' ') {
196                                 return NULL;
197                         }
198                         uri = req + 5;
199                         break;
200                         
201                 default:
202                         /* other CO method */
203                         return NULL;
204                 }
205                 break;
206                 
207         case 'T':
208                 /* TRACE method */
209                 if (req[1] != 'R' || req[2] != 'A' || req[3] != 'C' || req[4] != 'E' ||
210                     req[5] != ' ') {
211                         return NULL;
212                 }
213                 uri = req + 6;
214                 break;
215                 
216         case 'D':
217                 /* DELETE method */
218                 if (req[1] != 'E' || req[2] != 'L' || req[3] != 'E' || req[4] != 'T' ||
219                     req[5] != 'E' || req[6] != ' ') {
220                         return NULL;
221                 }
222                 uri = req + 7;
223                 break;
224                 
225         case 'L':
226                 /* LOCK method (WebDAV) */
227                 if (req[1] != 'O' || req[2] != 'C' || req[3] != 'K' || req[4] != ' ') {
228                         return NULL;
229                 }
230                 uri = req + 5;
231                 break;
232                 
233         case 'U':
234                 /* UNLOCK method */
235                 if (req[1] != 'N' || req[2] != 'L' || req[3] != 'O' || req[4] != 'C' ||
236                     req[5] != 'K' || req[6] != ' ') {
237                         return NULL;
238                 }
239                 uri = req + 7;
240                 break;
241                 
242         case 'M':
243                 switch(req[1])
244                 {
245                 case 'O':
246                         /* MOVE method (WebDAV) */
247                         if (req[2] != 'V' || req[3] != 'E' || req[4] != ' ') {
248                                 return NULL;
249                         }
250                         uri = req + 5;
251                         break;
252                         
253                 case 'K':
254                         /* MKCOL method (WebDAV) */
255                         if (req[2] != 'C' || req[3] != 'O' || req[4] != 'L' || req[5] != ' ') {
256                                 return NULL;
257                         }
258                         uri = req + 6;
259                         break;
260                         
261                 default:
262                         /* other M method */
263                         return NULL;
264                 }
265                 break;
266                         
267         default:
268                 /* other method */
269                 return NULL;
270         }
271
272         /*substitute ptr for uri */
273         ptr = uri;
274
275         /* looks for the 2nd blank address */
276         for(len_count = 0; *(ptr+len_count) != '\x0D' && *(ptr+len_count) != '\x0A' ; len_count++)
277         {
278                 if( *(ptr+len_count) == ' ' )
279                 {
280                         S = ptr+len_count;
281                         break;
282                 }
283
284                 /* Up to the length of the request */
285                 if(len_count == (len))
286                 {
287                         return NULL;
288                 }
289         }
290
291         /* there is no blank */
292         if(S == NULL)
293         {
294                 return NULL;
295         }
296
297         /* string check */
298         ret = strncmp(S, check_len, 8);
299
300         /* string is not equal */
301         if (ret != 0)
302         {
303                 return NULL;
304         }
305
306         /* ver is 1.0 or 1.1 */
307         if(S[8] !='0' && S[8]!='1')
308         {
309                 return NULL;
310         }
311
312         /* end is changing line. */
313         if(S[9] !='\x0D' || S[10]!='\x0A')
314         {
315                 return NULL;
316         }
317
318         *length = len_count;
319
320         return uri;
321 }
322
323  
324 /*!
325  * Search a HTTP header field.
326  * @param  http_header HTTP request/response header
327  * @param  field_name  HTTP header field name
328  * @return char points to field value of found header field, or NULL.
329  */
330 extern char*
331 http_search_header_field(char *http_header, char *field_name)
332 {
333         int check;
334         char *word_ptr;
335
336         /* check NULL */
337         if (http_header == NULL || field_name == NULL) {
338                 return NULL;
339         }
340
341         /* skip a HTTP method line or HTTP response status line */
342         word_ptr = http_skip_header_line(http_header);
343         
344         while (1) {
345                 /* word_ptr is always pointing to line top here */
346
347                 /* null check */
348                 if (word_ptr == NULL) {
349                         return NULL;
350                 }
351
352                 /* "\r\n\r" matched */
353                 if (*word_ptr == '\r') {
354                         return NULL;
355                 }
356
357                 /* compare header string and search name header */
358                 for (check = 0;
359                      toupper(word_ptr[check]) == toupper(field_name[check]) && field_name[check] != '\0';
360                      ++check);
361
362                 /* when header match the search name, next word of search name must be colon */
363                 /* cf. Accept':' and Accept'-'Language: */
364                 if (field_name[check] == '\0' && word_ptr[check] == ':') {
365                         /* matched search name header completely! */
366                         ++check;
367
368                         /* skip a space, tab, etc. */
369                         for ( ;
370                               !isgraph(word_ptr[check]) && word_ptr[check] != '\r' && word_ptr[check] != '\0';
371                               ++check );
372
373                         /* return pointer of searched name header's value */
374                         return &word_ptr[check];
375                 }
376
377                 /* go next line */
378                 word_ptr = http_skip_header_line(word_ptr);
379         }
380 }
381
382 /*!
383  * Insert any header field to HTTP header
384  * @param[in,out] header HTTP header strings
385  * @param[in] offset_length offset length
386  * @param[in] insert_field insert header field strings
387  * @param[in,out] header_length all of HTTP header length
388  * @retval 0  successfully insert header field
389  * @retval -1 some errors occur.
390  */
391 extern int
392 http_insert_field(char *header, int offset_length, char *insert_field, int header_length)
393 {
394         char *copy_buffer;
395
396         /* check args */
397         if (header == NULL)
398                 return -1;
399         if (insert_field == NULL)
400                 return -1;
401         if (offset_length < 0)
402                 return -1;
403         if (header_length < 0)
404                 return -1;
405         if (header_length < offset_length)
406                 return -1;
407
408         /* calloc buffer */
409         copy_buffer = (char *) calloc(1, header_length - offset_length);
410         if (copy_buffer == NULL)
411                 return -1;
412
413         /* backup strings */
414         memcpy(copy_buffer, header + offset_length, header_length - offset_length);
415
416         /* insert field */
417         memcpy(header + offset_length, insert_field, strlen(insert_field));
418
419         /* append backup strings and terminate null*/
420         memcpy(header + offset_length + strlen(insert_field), copy_buffer, header_length - offset_length);
421         header[offset_length + strlen(insert_field) + header_length - offset_length] = '\0';
422
423         /* free */
424         free(copy_buffer);
425         copy_buffer = NULL;
426
427         return 0;
428 }
429
430 /*!
431  * Skip current HTTP header line
432  * @param  top_ptr
433  * @return char    points to next line top. if there are no next, return NULL.
434  */
435 extern char*
436 http_skip_header_line(char *line)
437 {
438         char *top_ptr;
439
440         /* check null */
441         if (line == NULL) {
442                 return NULL;
443         }
444
445         top_ptr = (char *) line;
446         
447         /* skip until match '\r' or '\n' */
448         for ( ; *top_ptr != '\0' && *top_ptr != '\r'; ++top_ptr) {
449                 if (*top_ptr == '\n') {
450                         /* "\n" matched */
451                         ++top_ptr;
452                         return top_ptr;
453                 }
454         }
455
456         /* no next line */
457         if (*top_ptr == '\0') {
458                 return NULL;
459         }
460
461         /* '\r' matched */
462         /* then next word have to be '\n' */
463         ++top_ptr;
464         if (*top_ptr != '\n') {
465                 return NULL;
466         }
467
468         /* "\r\n" matched */
469         ++top_ptr;
470
471         return top_ptr;
472 }