]>
Commit | Line | Data |
---|---|---|
91503c21 AS |
1 | /* |
2 | * Copyright (C) 2013 Andreas Steffen | |
19ef2aec TB |
3 | * |
4 | * Copyright (C) secunet Security Networks AG | |
91503c21 AS |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2 of the License, or (at your | |
9 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
13 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 | * for more details. | |
15 | */ | |
16 | ||
17 | #define _GNU_SOURCE /* for asprintf() */ | |
18 | ||
19 | #include "tnc_ifmap_http.h" | |
20 | ||
21 | #include <utils/debug.h> | |
22 | #include <utils/lexparser.h> | |
23 | ||
24 | #include <stdio.h> | |
25 | ||
26 | typedef struct private_tnc_ifmap_http_t private_tnc_ifmap_http_t; | |
27 | ||
28 | /** | |
29 | * Private data of an tnc_ifmap_http_t object. | |
30 | */ | |
31 | struct private_tnc_ifmap_http_t { | |
32 | ||
33 | /** | |
34 | * Public tnc_ifmap_http_t interface. | |
35 | */ | |
36 | tnc_ifmap_http_t public; | |
37 | ||
38 | /** | |
39 | * HTTPS Server URI with https:// prefix removed | |
40 | */ | |
41 | char *uri; | |
42 | ||
43 | /** | |
44 | * Optional base64-encoded username:password for HTTP Basic Authentication | |
45 | */ | |
46 | chunk_t user_pass; | |
47 | ||
48 | /** | |
49 | * HTTP chunked mode | |
50 | */ | |
51 | bool chunked; | |
52 | ||
53 | }; | |
54 | ||
55 | METHOD(tnc_ifmap_http_t, build, status_t, | |
56 | private_tnc_ifmap_http_t *this, chunk_t *in, chunk_t *out) | |
57 | { | |
58 | char *host, *path, *request, auth[128]; | |
59 | int len; | |
60 | ||
61 | /* Duplicate host[/path] string since we are going to manipulate it */ | |
62 | len = strlen(this->uri) + 2; | |
63 | host = malloc(len); | |
64 | memset(host, '\0', len); | |
65 | strcpy(host, this->uri); | |
66 | ||
67 | /* Extract appended path or set to root */ | |
68 | path = strchr(host, '/'); | |
69 | if (!path) | |
70 | { | |
71 | path = host + len - 2; | |
72 | *path = '/'; | |
73 | } | |
74 | ||
75 | /* Use Basic Authentication? */ | |
76 | if (this->user_pass.len) | |
77 | { | |
78 | snprintf(auth, sizeof(auth), "Authorization: Basic %.*s\r\n", | |
0c76d820 | 79 | (int)this->user_pass.len, this->user_pass.ptr); |
91503c21 AS |
80 | } |
81 | else | |
82 | { | |
83 | *auth = '\0'; | |
84 | } | |
85 | ||
86 | /* Write HTTP POST request, TODO break up into chunks */ | |
87 | len = asprintf(&request, | |
88 | "POST %s HTTP/1.1\r\n" | |
89 | "Host: %.*s\r\n" | |
90 | "%s" | |
91 | "Content-Type: application/soap+xml;charset=utf-8\r\n" | |
92 | "Content-Length: %d\r\n" | |
93 | "\r\n" | |
0c76d820 TB |
94 | "%.*s", path, (int)(path-host), host, auth, (int)in->len, |
95 | (int)in->len, in->ptr); | |
91503c21 AS |
96 | free(host); |
97 | ||
98 | if (len == -1) | |
99 | { | |
100 | return FAILED; | |
101 | } | |
102 | *out = chunk_create(request, len); | |
3ea6fcb5 | 103 | DBG3(DBG_TLS, "sending HTTP POST request %B", out); |
91503c21 AS |
104 | |
105 | return SUCCESS; | |
106 | } | |
107 | ||
108 | static bool process_header(chunk_t *in, bool *chunked, u_int *content_len) | |
109 | { | |
110 | chunk_t line, version, parameter; | |
111 | int code; | |
112 | u_int len; | |
113 | ||
114 | /* Process HTTP protocol version */ | |
115 | if (!fetchline(in, &line) || !extract_token(&version, ' ', &line) || | |
116 | !match("HTTP/1.1", &version) || sscanf(line.ptr, "%d", &code) != 1) | |
117 | { | |
118 | DBG1(DBG_TNC, "malformed http response header"); | |
119 | return FALSE; | |
120 | } | |
121 | if (code != 200) | |
122 | { | |
123 | DBG1(DBG_TNC, "http response returns error code %d", code); | |
124 | return FALSE; | |
0c76d820 | 125 | } |
91503c21 AS |
126 | |
127 | *content_len = 0; | |
128 | *chunked = FALSE; | |
129 | ||
130 | /* Process HTTP header line by line until the HTTP body is reached */ | |
131 | while (fetchline(in, &line)) | |
132 | { | |
133 | if (line.len == 0) | |
134 | { | |
135 | break; | |
136 | } | |
137 | if (extract_token(¶meter, ':', &line) && eat_whitespace(&line)) | |
138 | { | |
139 | if (match("Content-Length", ¶meter)) | |
140 | { | |
141 | if (sscanf(line.ptr, "%u", &len) == 1) | |
142 | { | |
143 | *content_len = len; | |
144 | } | |
145 | } | |
146 | else if (match("Transfer-Encoding", ¶meter) && | |
147 | match("chunked", &line)) | |
148 | { | |
149 | *chunked = TRUE; | |
150 | } | |
151 | } | |
152 | } | |
153 | ||
154 | return TRUE; | |
155 | } | |
156 | ||
157 | METHOD(tnc_ifmap_http_t, process, status_t, | |
158 | private_tnc_ifmap_http_t *this, chunk_t *in, chunk_t *out) | |
159 | { | |
160 | u_int len = 0; | |
161 | chunk_t line, out_chunk; | |
162 | ||
163 | DBG3(DBG_TLS, "receiving HTTP response %B", in); | |
164 | ||
165 | if (!this->chunked) | |
166 | { | |
167 | if (!process_header(in, &this->chunked, &len)) | |
168 | { | |
169 | return FAILED; | |
170 | } | |
171 | } | |
172 | ||
173 | while (in->len) | |
174 | { | |
175 | if (this->chunked) | |
176 | { | |
177 | if (!fetchline(in, &line) || sscanf(line.ptr, "%x", &len) != 1) | |
178 | { | |
179 | return FAILED; | |
180 | } | |
181 | DBG3(DBG_TLS, "received HTTP response is chunked (%u bytes)", len); | |
182 | ||
183 | /* Received last chunk? */ | |
184 | if (len == 0) | |
185 | { | |
186 | return SUCCESS; | |
187 | } | |
188 | } | |
189 | ||
190 | /* Check size of of remaining HTTP body */ | |
191 | if (len > in->len) | |
192 | { | |
193 | DBG1(DBG_TNC, "insufficient data in HTTP body"); | |
194 | return FAILED; | |
195 | } | |
196 | ||
197 | if (this->chunked) | |
198 | { | |
199 | out_chunk = *in; | |
200 | out_chunk.len = len; | |
0c76d820 | 201 | *out = chunk_cat("mc", *out, out_chunk); |
91503c21 AS |
202 | *in = chunk_skip(*in, len); |
203 | if (!fetchline(in, &line) || line.len > 0) | |
204 | { | |
205 | return FAILED; | |
0c76d820 | 206 | } |
91503c21 AS |
207 | } |
208 | else | |
209 | { | |
210 | if (len) | |
211 | { | |
212 | in->len = len; | |
213 | } | |
214 | *out = chunk_clone(*in); | |
215 | return SUCCESS; | |
216 | } | |
217 | } | |
218 | return NEED_MORE; | |
219 | } | |
220 | ||
221 | METHOD(tnc_ifmap_http_t, destroy, void, | |
222 | private_tnc_ifmap_http_t *this) | |
223 | { | |
224 | free(this); | |
225 | } | |
226 | ||
227 | /** | |
228 | * See header | |
229 | */ | |
230 | tnc_ifmap_http_t *tnc_ifmap_http_create(char *uri, chunk_t user_pass) | |
231 | { | |
232 | private_tnc_ifmap_http_t *this; | |
233 | ||
234 | INIT(this, | |
235 | .public = { | |
236 | .build = _build, | |
237 | .process = _process, | |
238 | .destroy = _destroy, | |
239 | }, | |
240 | .uri = uri, | |
241 | .user_pass = user_pass, | |
242 | ); | |
243 | ||
244 | return &this->public; | |
245 | } | |
246 |