webservices: Add support for incoming TCP connections.
[wine.git] / dlls / webservices / listener.c
bloba82897e589c88cb1089fe49398e898675a1500f4
1 /*
2 * Copyright 2017 Hans Leidekker for CodeWeavers
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 #include <stdarg.h>
21 #include "windef.h"
22 #include "winbase.h"
23 #include "webservices.h"
25 #include "wine/debug.h"
26 #include "wine/list.h"
27 #include "wine/unicode.h"
28 #include "webservices_private.h"
29 #include "sock.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(webservices);
33 static BOOL winsock_loaded;
35 static BOOL WINAPI winsock_startup( INIT_ONCE *once, void *param, void **ctx )
37 int ret;
38 WSADATA data;
39 if (!(ret = WSAStartup( MAKEWORD(1,1), &data ))) winsock_loaded = TRUE;
40 else ERR( "WSAStartup failed: %d\n", ret );
41 return TRUE;
44 void winsock_init(void)
46 static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
47 InitOnceExecuteOnce( &once, winsock_startup, NULL, NULL );
50 static const struct prop_desc listener_props[] =
52 { sizeof(ULONG), FALSE }, /* WS_LISTENER_PROPERTY_LISTEN_BACKLOG */
53 { sizeof(WS_IP_VERSION), FALSE }, /* WS_LISTENER_PROPERTY_IP_VERSION */
54 { sizeof(WS_LISTENER_STATE), TRUE }, /* WS_LISTENER_PROPERTY_STATE */
55 { sizeof(WS_CALLBACK_MODEL), FALSE }, /* WS_LISTENER_PROPERTY_ASYNC_CALLBACK_MODEL */
56 { sizeof(WS_CHANNEL_TYPE), TRUE }, /* WS_LISTENER_PROPERTY_CHANNEL_TYPE */
57 { sizeof(WS_CHANNEL_BINDING), TRUE }, /* WS_LISTENER_PROPERTY_CHANNEL_BINDING */
58 { sizeof(ULONG), FALSE }, /* WS_LISTENER_PROPERTY_CONNECT_TIMEOUT */
59 { sizeof(BOOL), FALSE }, /* WS_LISTENER_PROPERTY_IS_MULTICAST */
60 { 0, FALSE }, /* WS_LISTENER_PROPERTY_MULTICAST_INTERFACES */
61 { sizeof(BOOL), FALSE }, /* WS_LISTENER_PROPERTY_MULTICAST_LOOPBACK */
62 { sizeof(ULONG), FALSE }, /* WS_LISTENER_PROPERTY_CLOSE_TIMEOUT */
63 { sizeof(ULONG), FALSE }, /* WS_LISTENER_PROPERTY_TO_HEADER_MATCHING_OPTIONS */
64 { sizeof(ULONG), FALSE }, /* WS_LISTENER_PROPERTY_TRANSPORT_URL_MATCHING_OPTIONS */
65 { sizeof(WS_CUSTOM_LISTENER_CALLBACKS), FALSE }, /* WS_LISTENER_PROPERTY_CUSTOM_LISTENER_CALLBACKS */
66 { 0, FALSE }, /* WS_LISTENER_PROPERTY_CUSTOM_LISTENER_PARAMETERS */
67 { sizeof(void *), TRUE }, /* WS_LISTENER_PROPERTY_CUSTOM_LISTENER_INSTANCE */
68 { sizeof(WS_DISALLOWED_USER_AGENT_SUBSTRINGS), FALSE } /* WS_LISTENER_PROPERTY_DISALLOWED_USER_AGENT */
71 struct listener
73 ULONG magic;
74 CRITICAL_SECTION cs;
75 WS_CHANNEL_TYPE type;
76 WS_CHANNEL_BINDING binding;
77 WS_LISTENER_STATE state;
78 union
80 struct
82 SOCKET socket;
83 } tcp;
84 } u;
85 ULONG prop_count;
86 struct prop prop[sizeof(listener_props)/sizeof(listener_props[0])];
89 #define LISTENER_MAGIC (('L' << 24) | ('I' << 16) | ('S' << 8) | 'T')
91 static struct listener *alloc_listener(void)
93 static const ULONG count = sizeof(listener_props)/sizeof(listener_props[0]);
94 struct listener *ret;
95 ULONG size = sizeof(*ret) + prop_size( listener_props, count );
97 if (!(ret = heap_alloc_zero( size ))) return NULL;
99 ret->magic = LISTENER_MAGIC;
100 InitializeCriticalSection( &ret->cs );
101 ret->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": listener.cs");
103 prop_init( listener_props, count, ret->prop, &ret[1] );
104 ret->prop_count = count;
105 return ret;
108 static void reset_listener( struct listener *listener )
110 listener->state = WS_LISTENER_STATE_CREATED;
112 switch (listener->binding)
114 case WS_TCP_CHANNEL_BINDING:
115 closesocket( listener->u.tcp.socket );
116 listener->u.tcp.socket = -1;
117 break;
119 default: break;
123 static void free_listener( struct listener *listener )
125 reset_listener( listener );
126 listener->cs.DebugInfo->Spare[0] = 0;
127 DeleteCriticalSection( &listener->cs );
128 heap_free( listener );
131 static HRESULT create_listener( WS_CHANNEL_TYPE type, WS_CHANNEL_BINDING binding,
132 const WS_LISTENER_PROPERTY *properties, ULONG count, struct listener **ret )
134 struct listener *listener;
135 HRESULT hr;
136 ULONG i;
138 if (!(listener = alloc_listener())) return E_OUTOFMEMORY;
140 for (i = 0; i < count; i++)
142 hr = prop_set( listener->prop, listener->prop_count, properties[i].id, properties[i].value,
143 properties[i].valueSize );
144 if (hr != S_OK)
146 free_listener( listener );
147 return hr;
151 listener->type = type;
152 listener->binding = binding;
154 switch (listener->binding)
156 case WS_TCP_CHANNEL_BINDING:
157 listener->u.tcp.socket = -1;
158 break;
160 default: break;
163 *ret = listener;
164 return S_OK;
167 /**************************************************************************
168 * WsCreateListener [webservices.@]
170 HRESULT WINAPI WsCreateListener( WS_CHANNEL_TYPE type, WS_CHANNEL_BINDING binding,
171 const WS_LISTENER_PROPERTY *properties, ULONG count,
172 const WS_SECURITY_DESCRIPTION *desc, WS_LISTENER **handle,
173 WS_ERROR *error )
175 struct listener *listener;
176 HRESULT hr;
178 TRACE( "%u %u %p %u %p %p %p\n", type, binding, properties, count, desc, handle, error );
179 if (error) FIXME( "ignoring error parameter\n" );
180 if (desc) FIXME( "ignoring security description\n" );
182 if (!handle) return E_INVALIDARG;
184 if (type != WS_CHANNEL_TYPE_DUPLEX_SESSION)
186 FIXME( "channel type %u not implemented\n", type );
187 return E_NOTIMPL;
189 if (binding != WS_TCP_CHANNEL_BINDING)
191 FIXME( "channel binding %u not implemented\n", binding );
192 return E_NOTIMPL;
195 if ((hr = create_listener( type, binding, properties, count, &listener )) != S_OK) return hr;
197 *handle = (WS_LISTENER *)listener;
198 return S_OK;
201 /**************************************************************************
202 * WsFreeListener [webservices.@]
204 void WINAPI WsFreeListener( WS_LISTENER *handle )
206 struct listener *listener = (struct listener *)handle;
208 TRACE( "%p\n", handle );
210 if (!listener) return;
212 EnterCriticalSection( &listener->cs );
214 if (listener->magic != LISTENER_MAGIC)
216 LeaveCriticalSection( &listener->cs );
217 return;
220 listener->magic = 0;
222 LeaveCriticalSection( &listener->cs );
223 free_listener( listener );
226 HRESULT resolve_hostname( const WCHAR *host, USHORT port, struct sockaddr *addr, int *addr_len )
228 static const WCHAR fmtW[] = {'%','u',0};
229 WCHAR service[6];
230 ADDRINFOW *res, *info;
231 HRESULT hr = WS_E_ADDRESS_NOT_AVAILABLE;
233 *addr_len = 0;
234 sprintfW( service, fmtW, port );
235 if (GetAddrInfoW( host, service, NULL, &res )) return HRESULT_FROM_WIN32( WSAGetLastError() );
237 info = res;
238 while (info && info->ai_family != AF_INET && info->ai_family != AF_INET6) info = info->ai_next;
239 if (info)
241 memcpy( addr, info->ai_addr, info->ai_addrlen );
242 *addr_len = info->ai_addrlen;
243 hr = S_OK;
246 FreeAddrInfoW( res );
247 return hr;
250 HRESULT parse_url( const WS_STRING *str, WS_URL_SCHEME_TYPE *scheme, WCHAR **host, USHORT *port )
252 WS_HEAP *heap;
253 WS_NETTCP_URL *url;
254 HRESULT hr;
256 if ((hr = WsCreateHeap( 1 << 8, 0, NULL, 0, &heap, NULL )) != S_OK) return hr;
257 if ((hr = WsDecodeUrl( str, 0, heap, (WS_URL **)&url, NULL )) != S_OK)
259 WsFreeHeap( heap );
260 return hr;
263 if (url->host.length == 1 && (url->host.chars[0] == '+' || url->host.chars[0] == '*')) *host = NULL;
264 else
266 if (!(*host = heap_alloc( (url->host.length + 1) * sizeof(WCHAR) )))
268 WsFreeHeap( heap );
269 return E_OUTOFMEMORY;
271 memcpy( *host, url->host.chars, url->host.length * sizeof(WCHAR) );
272 (*host)[url->host.length] = 0;
274 *scheme = url->url.scheme;
275 *port = url->port;
277 WsFreeHeap( heap );
278 return hr;
281 static HRESULT open_listener_tcp( struct listener *listener, const WS_STRING *url )
283 struct sockaddr_storage storage;
284 struct sockaddr *addr = (struct sockaddr *)&storage;
285 int addr_len;
286 WS_URL_SCHEME_TYPE scheme;
287 WCHAR *host;
288 USHORT port;
289 HRESULT hr;
291 if ((hr = parse_url( url, &scheme, &host, &port )) != S_OK) return hr;
292 if (scheme != WS_URL_NETTCP_SCHEME_TYPE)
294 heap_free( host );
295 return WS_E_INVALID_ENDPOINT_URL;
298 winsock_init();
300 hr = resolve_hostname( host, port, addr, &addr_len );
301 heap_free( host );
302 if (hr != S_OK) return hr;
304 if ((listener->u.tcp.socket = socket( addr->sa_family, SOCK_STREAM, 0 )) == -1)
305 return HRESULT_FROM_WIN32( WSAGetLastError() );
307 if (bind( listener->u.tcp.socket, addr, addr_len ) < 0)
309 closesocket( listener->u.tcp.socket );
310 listener->u.tcp.socket = -1;
311 return HRESULT_FROM_WIN32( WSAGetLastError() );
314 if (listen( listener->u.tcp.socket, 0 ) < 0)
316 closesocket( listener->u.tcp.socket );
317 listener->u.tcp.socket = -1;
318 return HRESULT_FROM_WIN32( WSAGetLastError() );
321 listener->state = WS_LISTENER_STATE_OPEN;
322 return S_OK;
325 static HRESULT open_listener( struct listener *listener, const WS_STRING *url )
327 switch (listener->binding)
329 case WS_TCP_CHANNEL_BINDING:
330 return open_listener_tcp( listener, url );
332 default:
333 ERR( "unhandled binding %u\n", listener->binding );
334 return E_NOTIMPL;
338 /**************************************************************************
339 * WsOpenListener [webservices.@]
341 HRESULT WINAPI WsOpenListener( WS_LISTENER *handle, WS_STRING *url, const WS_ASYNC_CONTEXT *ctx, WS_ERROR *error )
343 struct listener *listener = (struct listener *)handle;
344 HRESULT hr;
346 TRACE( "%p %s %p %p\n", handle, url ? debugstr_wn(url->chars, url->length) : "null", ctx, error );
347 if (error) FIXME( "ignoring error parameter\n" );
348 if (ctx) FIXME( "ignoring ctx parameter\n" );
350 if (!listener || !url) return E_INVALIDARG;
352 EnterCriticalSection( &listener->cs );
354 if (listener->magic != LISTENER_MAGIC)
356 LeaveCriticalSection( &listener->cs );
357 return E_INVALIDARG;
360 if (listener->state != WS_LISTENER_STATE_CREATED)
362 LeaveCriticalSection( &listener->cs );
363 return WS_E_INVALID_OPERATION;
366 hr = open_listener( listener, url );
368 LeaveCriticalSection( &listener->cs );
369 return hr;
372 static void close_listener( struct listener *listener )
374 reset_listener( listener );
375 listener->state = WS_LISTENER_STATE_CLOSED;
378 /**************************************************************************
379 * WsCloseListener [webservices.@]
381 HRESULT WINAPI WsCloseListener( WS_LISTENER *handle, const WS_ASYNC_CONTEXT *ctx, WS_ERROR *error )
383 struct listener *listener = (struct listener *)handle;
385 TRACE( "%p %p %p\n", handle, ctx, error );
386 if (error) FIXME( "ignoring error parameter\n" );
387 if (ctx) FIXME( "ignoring ctx parameter\n" );
389 if (!listener) return E_INVALIDARG;
391 EnterCriticalSection( &listener->cs );
393 if (listener->magic != LISTENER_MAGIC)
395 LeaveCriticalSection( &listener->cs );
396 return E_INVALIDARG;
399 close_listener( listener );
401 LeaveCriticalSection( &listener->cs );
402 return S_OK;
405 /**************************************************************************
406 * WsResetListener [webservices.@]
408 HRESULT WINAPI WsResetListener( WS_LISTENER *handle, WS_ERROR *error )
410 struct listener *listener = (struct listener *)handle;
412 TRACE( "%p %p\n", handle, error );
413 if (error) FIXME( "ignoring error parameter\n" );
415 if (!listener) return E_INVALIDARG;
417 EnterCriticalSection( &listener->cs );
419 if (listener->magic != LISTENER_MAGIC)
421 LeaveCriticalSection( &listener->cs );
422 return E_INVALIDARG;
425 if (listener->state != WS_LISTENER_STATE_CREATED && listener->state != WS_LISTENER_STATE_CLOSED)
427 LeaveCriticalSection( &listener->cs );
428 return WS_E_INVALID_OPERATION;
431 reset_listener( listener );
433 LeaveCriticalSection( &listener->cs );
434 return S_OK;
437 /**************************************************************************
438 * WsGetListenerProperty [webservices.@]
440 HRESULT WINAPI WsGetListenerProperty( WS_LISTENER *handle, WS_LISTENER_PROPERTY_ID id, void *buf,
441 ULONG size, WS_ERROR *error )
443 struct listener *listener = (struct listener *)handle;
444 HRESULT hr = S_OK;
446 TRACE( "%p %u %p %u %p\n", handle, id, buf, size, error );
447 if (error) FIXME( "ignoring error parameter\n" );
449 if (!listener) return E_INVALIDARG;
451 EnterCriticalSection( &listener->cs );
453 if (listener->magic != LISTENER_MAGIC)
455 LeaveCriticalSection( &listener->cs );
456 return E_INVALIDARG;
459 switch (id)
461 case WS_LISTENER_PROPERTY_STATE:
462 if (!buf || size != sizeof(listener->state)) hr = E_INVALIDARG;
463 else *(WS_LISTENER_STATE *)buf = listener->state;
464 break;
466 case WS_LISTENER_PROPERTY_CHANNEL_TYPE:
467 if (!buf || size != sizeof(listener->type)) hr = E_INVALIDARG;
468 else *(WS_CHANNEL_TYPE *)buf = listener->type;
469 break;
471 case WS_LISTENER_PROPERTY_CHANNEL_BINDING:
472 if (!buf || size != sizeof(listener->binding)) hr = E_INVALIDARG;
473 else *(WS_CHANNEL_BINDING *)buf = listener->binding;
474 break;
476 default:
477 hr = prop_get( listener->prop, listener->prop_count, id, buf, size );
480 LeaveCriticalSection( &listener->cs );
481 return hr;
484 /**************************************************************************
485 * WsSetListenerProperty [webservices.@]
487 HRESULT WINAPI WsSetListenerProperty( WS_LISTENER *handle, WS_LISTENER_PROPERTY_ID id, const void *value,
488 ULONG size, WS_ERROR *error )
490 struct listener *listener = (struct listener *)handle;
491 HRESULT hr;
493 TRACE( "%p %u %p %u\n", handle, id, value, size );
494 if (error) FIXME( "ignoring error parameter\n" );
496 if (!listener) return E_INVALIDARG;
498 EnterCriticalSection( &listener->cs );
500 if (listener->magic != LISTENER_MAGIC)
502 LeaveCriticalSection( &listener->cs );
503 return E_INVALIDARG;
506 hr = prop_set( listener->prop, listener->prop_count, id, value, size );
508 LeaveCriticalSection( &listener->cs );
509 return hr;
512 /**************************************************************************
513 * WsAcceptChannel [webservices.@]
515 HRESULT WINAPI WsAcceptChannel( WS_LISTENER *handle, WS_CHANNEL *channel_handle, const WS_ASYNC_CONTEXT *ctx,
516 WS_ERROR *error )
518 struct listener *listener = (struct listener *)handle;
519 HRESULT hr;
521 TRACE( "%p %p %p %p\n", handle, channel_handle, ctx, error );
522 if (error) FIXME( "ignoring error parameter\n" );
523 if (ctx) FIXME( "ignoring ctx parameter\n" );
525 if (!listener || !channel_handle) return E_INVALIDARG;
527 EnterCriticalSection( &listener->cs );
529 if (listener->magic != LISTENER_MAGIC)
531 LeaveCriticalSection( &listener->cs );
532 return E_INVALIDARG;
535 if (listener->state != WS_LISTENER_STATE_OPEN)
537 LeaveCriticalSection( &listener->cs );
538 return WS_E_INVALID_OPERATION;
541 switch (listener->binding)
543 case WS_TCP_CHANNEL_BINDING:
544 hr = channel_accept_tcp( listener->u.tcp.socket, channel_handle );
545 break;
547 default:
548 FIXME( "listener binding %u not supported\n", listener->binding );
549 hr = E_NOTIMPL;
550 break;
553 LeaveCriticalSection( &listener->cs );
554 return hr;