2 * Copyright 2019 Zebediah Figura
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 /* We have to return the HTTP_REQUEST structure to userspace exactly as it will
20 * be consumed; httpapi has no opportunity to massage it. Since it contains
21 * pointers, this is somewhat nontrivial. */
23 struct http_unknown_header
26 USHORT RawValueLength
;
27 POINTER pName
; /* char string */
28 POINTER pRawValue
; /* char string */
31 struct http_data_chunk
33 HTTP_DATA_CHUNK_TYPE DataChunkType
;
38 POINTER pBuffer
; /* char string */
41 /* for the struct size */
44 ULARGE_INTEGER StartingOffset
;
45 ULARGE_INTEGER Length
;
54 HTTP_CONNECTION_ID ConnectionId
;
55 HTTP_REQUEST_ID RequestId
;
56 HTTP_URL_CONTEXT UrlContext
;
59 USHORT UnknownVerbLength
;
61 POINTER pUnknownVerb
; /* char string */
62 POINTER pRawUrl
; /* char string */
68 USHORT QueryStringLength
;
69 POINTER pFullUrl
; /* WCHAR string */
70 POINTER pHost
; /* pointer to above */
71 POINTER pAbsPath
; /* pointer to above */
72 POINTER pQueryString
; /* pointer to above */
76 POINTER pRemoteAddress
; /* SOCKADDR */
77 POINTER pLocalAddress
; /* SOCKADDR */
81 USHORT UnknownHeaderCount
;
82 POINTER pUnknownHeaders
; /* struct http_unknown_header */
84 POINTER pTrailers
; /* NULL */
87 USHORT RawValueLength
;
88 POINTER pRawValue
; /* char string */
89 } KnownHeaders
[HttpHeaderRequestMaximum
];
91 ULONGLONG BytesReceived
;
92 USHORT EntityChunkCount
;
93 POINTER pEntityChunks
; /* struct http_data_chunk */
94 HTTP_RAW_CONNECTION_ID RawConnectionId
;
95 POINTER pSslInfo
; /* NULL (FIXME) */
96 USHORT RequestInfoCount
;
97 POINTER pRequestInfo
; /* NULL (FIXME) */
100 static NTSTATUS
complete_irp(struct connection
*conn
, IRP
*irp
)
102 const struct http_receive_request_params params
103 = *(struct http_receive_request_params
*)irp
->AssociatedIrp
.SystemBuffer
;
104 ULONG cooked_len
, host_len
, abs_path_len
, query_len
, chunk_len
= 0, offset
, processed
;
105 IO_STACK_LOCATION
*stack
= IoGetCurrentIrpStackLocation(irp
);
106 const DWORD output_len
= stack
->Parameters
.DeviceIoControl
.OutputBufferLength
;
107 struct http_request
*req
= irp
->AssociatedIrp
.SystemBuffer
;
108 const char *p
, *name
, *value
, *host
, *abs_path
, *query
;
109 struct http_unknown_header
*unk_headers
= NULL
;
110 char *buffer
= irp
->AssociatedIrp
.SystemBuffer
;
111 DWORD irp_size
= sizeof(struct http_request
);
112 USHORT unk_headers_count
= 0, unk_header_idx
;
113 struct http_data_chunk
*chunk
= NULL
;
114 int name_len
, value_len
, len
;
115 struct sockaddr_in addr
;
117 /* First calculate the total buffer size needed for this IRP. */
119 if (conn
->unk_verb_len
)
120 irp_size
+= conn
->unk_verb_len
+ 1;
121 irp_size
+= conn
->url_len
+ 1;
124 if (conn
->url
[0] == '/')
126 p
= host
= conn
->host
;
127 while (isgraph(*p
)) ++p
;
128 host_len
= p
- conn
->host
;
129 abs_path
= conn
->url
;
130 abs_path_len
= conn
->url_len
;
134 host
= conn
->url
+ 7;
135 abs_path
= strchr(host
, '/');
136 host_len
= abs_path
- host
;
137 abs_path_len
= (conn
->url
+ conn
->url_len
) - abs_path
;
139 if ((query
= memchr(abs_path
, '?', abs_path_len
)))
141 query_len
= (abs_path
+ abs_path_len
) - query
;
142 abs_path_len
= query
- abs_path
;
146 cooked_len
= (7 /* scheme */ + host_len
+ abs_path_len
+ query_len
) * sizeof(WCHAR
);
147 irp_size
+= cooked_len
+ sizeof(WCHAR
);
150 irp_size
+= 2 * sizeof(addr
);
153 p
= strstr(conn
->buffer
, "\r\n") + 2;
154 while (memcmp(p
, "\r\n", 2))
157 parse_header(name
, &name_len
, &value
, &value_len
);
158 if (parse_header_name(name
, name_len
) == HttpHeaderRequestMaximum
)
160 irp_size
+= name_len
+ 1;
163 irp_size
+= value_len
+ 1;
164 p
= strstr(p
, "\r\n") + 2;
168 irp_size
+= unk_headers_count
* sizeof(struct http_unknown_header
);
170 TRACE("Need %u bytes, have %u.\n", irp_size
, output_len
);
171 irp
->IoStatus
.Information
= irp_size
;
173 memset(irp
->AssociatedIrp
.SystemBuffer
, 0, output_len
);
175 if (output_len
< irp_size
)
177 req
->ConnectionId
= (ULONG_PTR
)conn
;
178 req
->RequestId
= conn
->req_id
;
179 return STATUS_BUFFER_OVERFLOW
;
182 offset
= sizeof(*req
);
184 req
->ConnectionId
= (ULONG_PTR
)conn
;
185 req
->RequestId
= conn
->req_id
;
186 req
->UrlContext
= conn
->queue
->context
;
187 req
->Version
= conn
->version
;
188 req
->Verb
= conn
->verb
;
189 req
->UnknownVerbLength
= conn
->unk_verb_len
;
190 req
->RawUrlLength
= conn
->url_len
;
192 if (conn
->unk_verb_len
)
194 req
->pUnknownVerb
= params
.addr
+ offset
;
195 memcpy(buffer
+ offset
, conn
->buffer
, conn
->unk_verb_len
);
196 offset
+= conn
->unk_verb_len
;
197 buffer
[offset
++] = 0;
200 req
->pRawUrl
= params
.addr
+ offset
;
201 memcpy(buffer
+ offset
, conn
->url
, conn
->url_len
);
202 offset
+= conn
->url_len
;
203 buffer
[offset
++] = 0;
205 req
->CookedUrl
.FullUrlLength
= cooked_len
;
206 req
->CookedUrl
.HostLength
= host_len
* sizeof(WCHAR
);
207 req
->CookedUrl
.AbsPathLength
= abs_path_len
* sizeof(WCHAR
);
208 req
->CookedUrl
.QueryStringLength
= query_len
* sizeof(WCHAR
);
209 req
->CookedUrl
.pFullUrl
= params
.addr
+ offset
;
210 req
->CookedUrl
.pHost
= req
->CookedUrl
.pFullUrl
+ 7 * sizeof(WCHAR
);
211 req
->CookedUrl
.pAbsPath
= req
->CookedUrl
.pHost
+ host_len
* sizeof(WCHAR
);
213 req
->CookedUrl
.pQueryString
= req
->CookedUrl
.pAbsPath
+ abs_path_len
* sizeof(WCHAR
);
215 memcpy(buffer
+ offset
, L
"http://", sizeof(L
"http://"));
216 offset
+= 7 * sizeof(WCHAR
);
217 MultiByteToWideChar(CP_ACP
, 0, host
, host_len
, (WCHAR
*)(buffer
+ offset
), host_len
* sizeof(WCHAR
));
218 offset
+= host_len
* sizeof(WCHAR
);
219 MultiByteToWideChar(CP_ACP
, 0, abs_path
, abs_path_len
+ query_len
,
220 (WCHAR
*)(buffer
+ offset
), (abs_path_len
+ query_len
) * sizeof(WCHAR
));
221 offset
+= (abs_path_len
+ query_len
) * sizeof(WCHAR
);
222 buffer
[offset
++] = 0;
223 buffer
[offset
++] = 0;
225 req
->Address
.pRemoteAddress
= params
.addr
+ offset
;
227 getpeername(conn
->socket
, (struct sockaddr
*)&addr
, &len
);
228 memcpy(buffer
+ offset
, &addr
, sizeof(addr
));
229 offset
+= sizeof(addr
);
231 req
->Address
.pLocalAddress
= params
.addr
+ offset
;
233 getsockname(conn
->socket
, (struct sockaddr
*)&addr
, &len
);
234 memcpy(buffer
+ offset
, &addr
, sizeof(addr
));
235 offset
+= sizeof(addr
);
237 req
->Headers
.UnknownHeaderCount
= unk_headers_count
;
238 if (unk_headers_count
)
240 req
->Headers
.pUnknownHeaders
= params
.addr
+ offset
;
241 unk_headers
= (struct http_unknown_header
*)(buffer
+ offset
);
242 offset
+= unk_headers_count
* sizeof(*unk_headers
);
246 p
= strstr(conn
->buffer
, "\r\n") + 2;
247 while (memcmp(p
, "\r\n", 2))
252 parse_header(name
, &name_len
, &value
, &value_len
);
253 if ((id
= parse_header_name(name
, name_len
)) == HttpHeaderRequestMaximum
)
255 unk_headers
[unk_header_idx
].NameLength
= name_len
;
256 unk_headers
[unk_header_idx
].RawValueLength
= value_len
;
257 unk_headers
[unk_header_idx
].pName
= params
.addr
+ offset
;
258 memcpy(buffer
+ offset
, name
, name_len
);
260 buffer
[offset
++] = 0;
261 unk_headers
[unk_header_idx
].pRawValue
= params
.addr
+ offset
;
262 memcpy(buffer
+ offset
, value
, value_len
);
264 buffer
[offset
++] = 0;
269 req
->Headers
.KnownHeaders
[id
].RawValueLength
= value_len
;
270 req
->Headers
.KnownHeaders
[id
].pRawValue
= params
.addr
+ offset
;
271 memcpy(buffer
+ offset
, value
, value_len
);
273 buffer
[offset
++] = 0;
275 p
= strstr(p
, "\r\n") + 2;
279 if (irp_size
+ sizeof(*chunk
) < output_len
&& (params
.flags
& HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY
))
280 chunk_len
= min(conn
->content_len
, output_len
- (irp_size
+ sizeof(*chunk
)));
283 req
->EntityChunkCount
= 1;
284 req
->pEntityChunks
= params
.addr
+ offset
;
285 chunk
= (struct http_data_chunk
*)(buffer
+ offset
);
286 offset
+= sizeof(*chunk
);
287 chunk
->DataChunkType
= HttpDataChunkFromMemory
;
288 chunk
->FromMemory
.BufferLength
= chunk_len
;
289 chunk
->FromMemory
.pBuffer
= params
.addr
+ offset
;
290 memcpy(buffer
+ offset
, p
, chunk_len
);
293 irp
->IoStatus
.Information
= irp_size
+ sizeof(*chunk
) + chunk_len
;
296 if (chunk_len
< conn
->content_len
)
297 req
->Flags
|= HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS
;
299 req
->BytesReceived
= conn
->req_len
;
301 assert(offset
== irp
->IoStatus
.Information
);
303 conn
->available
= FALSE
;
304 processed
= conn
->req_len
- (conn
->content_len
- chunk_len
);
305 memmove(conn
->buffer
, conn
->buffer
+ processed
, conn
->len
- processed
);
306 conn
->content_len
-= chunk_len
;
307 conn
->len
-= processed
;
309 return STATUS_SUCCESS
;