/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** NT Pipes network wrapper * * @author Mladen Turk * @version $Revision: 466585 $, $Date: 2006-10-22 00:16:34 +0200 (Sun, 22 Oct 2006) $ */ #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0500 #endif #define STRICT #include #include #include #include #include "tcn.h" #include "apr_thread_mutex.h" #include "apr_poll.h" #ifdef TCN_DO_STATISTICS #include "apr_atomic.h" static volatile apr_uint32_t ntp_created = 0; static volatile apr_uint32_t ntp_closed = 0; static volatile apr_uint32_t ntp_cleared = 0; static volatile apr_uint32_t ntp_accepted = 0; void ntp_network_dump_statistics() { fprintf(stderr, "NT Network Statistics ..\n"); fprintf(stderr, "Sockets created : %d\n", ntp_created); fprintf(stderr, "Sockets accepted : %d\n", ntp_accepted); fprintf(stderr, "Sockets closed : %d\n", ntp_closed); fprintf(stderr, "Sockets cleared : %d\n", ntp_cleared); } #endif #define DEFNAME "\\\\.\\PIPE\\TOMCATNATIVEPIPE" #define DEFNAME_FMT "\\\\.\\PIPE\\TOMCATNATIVEPIPE%08X%08X" #define DEFSIZE 8192 #define DEFTIMEOUT 60000 #define TCN_NTP_UNKNOWN 0 #define TCN_NTP_CLIENT 1 #define TCN_NTP_SERVER 2 typedef struct { apr_pool_t *pool; apr_socket_t *sock; /* Dummy socket */ OVERLAPPED rd_o; OVERLAPPED wr_o; HANDLE h_pipe; HANDLE rd_event; HANDLE wr_event; DWORD timeout; int mode; /* Client or server mode */ int nmax; DWORD sndbuf; DWORD rcvbuf; char name[MAX_PATH+1]; SECURITY_ATTRIBUTES sa; } tcn_ntp_conn_t; static const char *NTSD_STRING = "D:" /* Discretionary ACL */ "(D;OICI;GA;;;BG)" /* Deny access to Built-in Guests */ "(D;OICI;GA;;;AN)" /* Deny access to Anonymous Logon */ "(A;OICI;GRGWGX;;;AU)" /* Allow read/write/execute to Authenticated Users */ "(A;OICI;GA;;;BA)" /* Allow full control to Administrators */ "(A;OICI;GA;;;LS)" /* Allow full control to Local service account */ "(A;OICI;GA;;;SY)"; /* Allow full control to Local system */ static apr_status_t APR_THREAD_FUNC ntp_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t) { tcn_ntp_conn_t *con = (tcn_ntp_conn_t *)sock; if (t < 0) con->timeout = INFINITE; else con->timeout = (DWORD)(apr_time_as_msec(t)); return APR_SUCCESS; } static apr_status_t APR_THREAD_FUNC ntp_socket_timeout_get(apr_socket_t *sock, apr_interval_time_t *t) { tcn_ntp_conn_t *con = (tcn_ntp_conn_t*)sock; if (con->timeout == INFINITE) *t = -1; else *t = con->timeout * 1000; return APR_SUCCESS; } static APR_INLINE apr_status_t APR_THREAD_FUNC ntp_socket_opt_set(apr_socket_t *sock, apr_int32_t opt, apr_int32_t on) { tcn_ntp_conn_t *con = (tcn_ntp_conn_t *)sock; apr_status_t rv = APR_SUCCESS; switch (opt) { case APR_SO_SNDBUF: con->sndbuf = (DWORD)on; break; case APR_SO_RCVBUF: con->rcvbuf = (DWORD)on; break; default: rv = APR_EINVAL; break; } return rv; } static APR_INLINE apr_status_t APR_THREAD_FUNC ntp_socket_opt_get(apr_socket_t *sock, apr_int32_t opt, apr_int32_t *on) { tcn_ntp_conn_t *con = (tcn_ntp_conn_t *)sock; apr_status_t rv = APR_SUCCESS; switch (opt) { case APR_SO_SNDBUF: *on = con->sndbuf; break; case APR_SO_RCVBUF: *on = con->rcvbuf; break; default: rv = APR_EINVAL; break; } return rv; } static apr_status_t ntp_cleanup(void *data) { tcn_ntp_conn_t *con = (tcn_ntp_conn_t *)data; if (con) { if (con->h_pipe) { FlushFileBuffers(con->h_pipe); CloseHandle(con->h_pipe); con->h_pipe = NULL; } if (con->rd_event) { CloseHandle(con->rd_event); con->rd_event = NULL; } if (con->wr_event) { CloseHandle(con->wr_event); con->wr_event= NULL; } } #ifdef TCN_DO_STATISTICS apr_atomic_inc32(&ntp_cleared); #endif return APR_SUCCESS; } static apr_status_t APR_THREAD_FUNC ntp_socket_shutdown(apr_socket_t *sock, apr_shutdown_how_e how) { UNREFERENCED(how); return ntp_cleanup(sock);; } static apr_status_t APR_THREAD_FUNC ntp_socket_close(apr_socket_t *sock) { #ifdef TCN_DO_STATISTICS apr_atomic_inc32(&ntp_closed); #endif return ntp_cleanup(sock);; } static apr_status_t APR_THREAD_FUNC ntp_socket_recv(apr_socket_t *sock, char *buf, apr_size_t *len) { tcn_ntp_conn_t *con = (tcn_ntp_conn_t *)sock; DWORD readed; if (!ReadFile(con->h_pipe, buf, *len, &readed, &con->rd_o)) { DWORD err = GetLastError(); if (err == ERROR_IO_PENDING) { DWORD r = WaitForSingleObject(con->rd_event, con->timeout); if (r == WAIT_TIMEOUT) return APR_TIMEUP; else if (r != WAIT_OBJECT_0) return APR_EOF; } else if (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA) { /* Server closed the pipe */ return APR_EOF; } GetOverlappedResult(con->h_pipe, &con->rd_o, &readed, FALSE); } *len = readed; return APR_SUCCESS; } static apr_status_t APR_THREAD_FUNC ntp_socket_send(apr_socket_t *sock, const char *buf, apr_size_t *len) { tcn_ntp_conn_t *con = (tcn_ntp_conn_t *)sock; DWORD written; if (!WriteFile(con->h_pipe, buf, *len, &written, &con->wr_o)) { DWORD err = GetLastError(); if (err == ERROR_IO_PENDING) { DWORD r = WaitForSingleObject(con->wr_event, con->timeout); if (r == WAIT_TIMEOUT) return APR_TIMEUP; else if (r != WAIT_OBJECT_0) return APR_EOF; } else if (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA) { /* Server closed the pipe */ return APR_EOF; } GetOverlappedResult(con->h_pipe, &con->wr_o, &written, FALSE); } *len = written; return APR_SUCCESS; } static apr_status_t APR_THREAD_FUNC ntp_socket_sendv(apr_socket_t *sock, const struct iovec *vec, apr_int32_t nvec, apr_size_t *len) { tcn_ntp_conn_t *con = (tcn_ntp_conn_t *)sock; apr_status_t rv; apr_size_t written = 0; apr_int32_t i; for (i = 0; i < nvec; i++) { apr_size_t rd = vec[i].iov_len; if ((rv = ntp_socket_send((apr_socket_t *)con, vec[i].iov_base, &rd)) != APR_SUCCESS) { *len = written; return rv; } written += rd; } *len = written; return APR_SUCCESS; } static apr_status_t ntp_socket_cleanup(void *data) { tcn_socket_t *s = (tcn_socket_t *)data; if (s->net->cleanup) { (*s->net->cleanup)(s->opaque); s->net->cleanup = NULL; } #ifdef TCN_DO_STATISTICS apr_atomic_inc32(&ntp_cleared); #endif return APR_SUCCESS; } static tcn_nlayer_t ntp_socket_layer = { TCN_SOCKET_NTPIPE, ntp_cleanup, ntp_socket_close, ntp_socket_shutdown, ntp_socket_opt_get, ntp_socket_opt_set, ntp_socket_timeout_get, ntp_socket_timeout_set, ntp_socket_send, ntp_socket_sendv, ntp_socket_recv }; static BOOL create_DACL(LPSECURITY_ATTRIBUTES psa) { return ConvertStringSecurityDescriptorToSecurityDescriptor( NTSD_STRING, SDDL_REVISION_1, &(psa->lpSecurityDescriptor), NULL); } TCN_IMPLEMENT_CALL(jlong, Local, create)(TCN_STDARGS, jstring name, jlong pool) { apr_pool_t *p = J2P(pool, apr_pool_t *); tcn_socket_t *s = NULL; tcn_ntp_conn_t *con = NULL; TCN_ALLOC_CSTRING(name); UNREFERENCED(o); TCN_ASSERT(pool != 0); #ifdef TCN_DO_STATISTICS ntp_created++; #endif con = (tcn_ntp_conn_t *)apr_pcalloc(p, sizeof(tcn_ntp_conn_t)); con->pool = p; con->mode = TCN_NTP_UNKNOWN; con->nmax = PIPE_UNLIMITED_INSTANCES; con->timeout = DEFTIMEOUT; con->sndbuf = DEFSIZE; con->rcvbuf = DEFSIZE; if (J2S(name)) { strncpy(con->name, J2S(name), MAX_PATH); con->name[MAX_PATH] = '\0'; TCN_FREE_CSTRING(name); } else strcpy(con->name, DEFNAME); con->sa.nLength = sizeof(con->sa); con->sa.bInheritHandle = TRUE; if (!create_DACL(&con->sa)) { tcn_ThrowAPRException(e, apr_get_os_error()); return 0; } s = (tcn_socket_t *)apr_pcalloc(p, sizeof(tcn_socket_t)); s->pool = p; s->net = &ntp_socket_layer; s->opaque = con; apr_pool_cleanup_register(p, (const void *)s, ntp_socket_cleanup, apr_pool_cleanup_null); fflush(stderr); return P2J(s); } TCN_IMPLEMENT_CALL(jint, Local, bind)(TCN_STDARGS, jlong sock, jlong sa) { tcn_socket_t *s = J2P(sock, tcn_socket_t *); UNREFERENCED_STDARGS; UNREFERENCED(sa); TCN_ASSERT(sock != 0); if (s->net->type == TCN_SOCKET_NTPIPE) { tcn_ntp_conn_t *c = (tcn_ntp_conn_t *)s->opaque; c->mode = TCN_NTP_SERVER; return APR_SUCCESS; } else return APR_EINVAL; } TCN_IMPLEMENT_CALL(jint, Local, listen)(TCN_STDARGS, jlong sock, jint backlog) { tcn_socket_t *s = J2P(sock, tcn_socket_t *); UNREFERENCED_STDARGS; TCN_ASSERT(sock != 0); if (s->net->type == TCN_SOCKET_NTPIPE) { tcn_ntp_conn_t *c = (tcn_ntp_conn_t *)s->opaque; c->mode = TCN_NTP_SERVER; if (backlog > 0) c->nmax = backlog; else c->nmax = PIPE_UNLIMITED_INSTANCES; return APR_SUCCESS; } else return APR_EINVAL; } TCN_IMPLEMENT_CALL(jlong, Local, accept)(TCN_STDARGS, jlong sock) { tcn_socket_t *s = J2P(sock, tcn_socket_t *); apr_pool_t *p = NULL; tcn_socket_t *a = NULL; tcn_ntp_conn_t *con = NULL; UNREFERENCED(o); TCN_ASSERT(sock != 0); TCN_THROW_IF_ERR(apr_pool_create(&p, s->pool), p); if (s->net->type == TCN_SOCKET_NTPIPE) { tcn_ntp_conn_t *c = (tcn_ntp_conn_t *)s->opaque; con = (tcn_ntp_conn_t *)apr_pcalloc(p, sizeof(tcn_ntp_conn_t)); con->pool = p; con->mode = TCN_NTP_SERVER; con->nmax = c->nmax; con->timeout = c->timeout; strcpy(con->name, c->name); con->h_pipe = CreateNamedPipe(con->name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, con->nmax, con->sndbuf, con->rcvbuf, con->timeout, &c->sa); if (con->h_pipe == INVALID_HANDLE_VALUE) { tcn_ThrowAPRException(e, apr_get_os_error()); goto cleanup; } /* Block until a client connects */ if (!ConnectNamedPipe(con->h_pipe, NULL)) { DWORD err = GetLastError(); if (err != ERROR_PIPE_CONNECTED) { CloseHandle(con->h_pipe); tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(err)); goto cleanup; } } /* Create overlapped events */ con->rd_event = CreateEvent(NULL, TRUE, FALSE, NULL); con->rd_o.hEvent = con->rd_event; con->wr_event = CreateEvent(NULL, TRUE, FALSE, NULL); con->wr_o.hEvent = con->wr_event; } else { tcn_ThrowAPRException(e, APR_ENOTIMPL); goto cleanup; } if (con) { #ifdef TCN_DO_STATISTICS apr_atomic_inc32(&ntp_accepted); #endif a = (tcn_socket_t *)apr_pcalloc(p, sizeof(tcn_socket_t)); a->pool = p; a->net = &ntp_socket_layer; a->opaque = con; apr_pool_cleanup_register(p, (const void *)a, ntp_socket_cleanup, apr_pool_cleanup_null); } return P2J(a); cleanup: if (p) apr_pool_destroy(p); return 0; } TCN_IMPLEMENT_CALL(jint, Local, connect)(TCN_STDARGS, jlong sock, jlong sa) { tcn_socket_t *s = J2P(sock, tcn_socket_t *); apr_pool_t *p = NULL; tcn_socket_t *a = NULL; tcn_ntp_conn_t *con = NULL; UNREFERENCED(o); UNREFERENCED(sa); TCN_ASSERT(sock != 0); if (s->net->type != TCN_SOCKET_NTPIPE) return APR_ENOTSOCK; con = (tcn_ntp_conn_t *)s->opaque; if (con->mode == TCN_NTP_SERVER) return APR_EINVAL; con->mode = TCN_NTP_CLIENT; while (TRUE) { con->h_pipe = CreateFile(con->name, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE , NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (con->h_pipe != INVALID_HANDLE_VALUE) break; if (GetLastError() == ERROR_PIPE_BUSY) { /* All pipe instances are busy, so wait for * timeout value specified by the server process in * the CreateNamedPipe function. */ if (!WaitNamedPipe(con->name, NMPWAIT_USE_DEFAULT_WAIT)) return apr_get_os_error(); } else return apr_get_os_error(); } /* Create overlapped events */ con->rd_event = CreateEvent(NULL, TRUE, FALSE, NULL); con->rd_o.hEvent = con->rd_event; con->wr_event = CreateEvent(NULL, TRUE, FALSE, NULL); con->wr_o.hEvent = con->wr_event; return APR_SUCCESS; }