diff options
Diffstat (limited to 'src/3rdparty/libdbus/dbus/dbus-sysdeps-win.c')
-rw-r--r-- | src/3rdparty/libdbus/dbus/dbus-sysdeps-win.c | 4521 |
1 files changed, 4521 insertions, 0 deletions
diff --git a/src/3rdparty/libdbus/dbus/dbus-sysdeps-win.c b/src/3rdparty/libdbus/dbus/dbus-sysdeps-win.c new file mode 100644 index 00000000..d44dc0a9 --- /dev/null +++ b/src/3rdparty/libdbus/dbus/dbus-sysdeps-win.c @@ -0,0 +1,4521 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-sysdeps.c Wrappers around system/libc features (internal to D-BUS implementation) + * + * Copyright (C) 2002, 2003 Red Hat, Inc. + * Copyright (C) 2003 CodeFactory AB + * Copyright (C) 2005 Novell, Inc. + * Copyright (C) 2006 Peter Kümmel <syntheticpp@gmx.net> + * Copyright (C) 2006 Christian Ehrlicher <ch.ehrlicher@gmx.de> + * Copyright (C) 2006-2021 Ralf Habacker <ralf.habacker@freenet.de> + * + * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#define STRSAFE_NO_DEPRECATE + +#include "dbus-internals.h" +#include "dbus-sha.h" +#include "dbus-sysdeps.h" +#include "dbus-threads.h" +#include "dbus-protocol.h" +#include "dbus-string.h" +#include "dbus-sysdeps.h" +#include "dbus-sysdeps-win.h" +#include "dbus-protocol.h" +#include "dbus-hash.h" +#include "dbus-sockets-win.h" +#include "dbus-list.h" +#include "dbus-nonce.h" +#include "dbus-credentials.h" + +#include <windows.h> +#include <wincrypt.h> +#include <iphlpapi.h> +#ifdef HAVE_AFUNIX_H +#include <afunix.h> +#endif + +/* Declarations missing in mingw's and windows sdk 7.0 headers */ +extern BOOL WINAPI ConvertStringSidToSidA (LPCSTR StringSid, PSID *Sid); +extern BOOL WINAPI ConvertSidToStringSidA (PSID Sid, LPSTR *StringSid); + +#include <stdio.h> +#include <stdlib.h> + +#include <string.h> +#if HAVE_ERRNO_H +#include <errno.h> +#endif +#ifndef DBUS_WINCE +#include <mbstring.h> +#include <sys/stat.h> +#include <sys/types.h> +#endif + +#ifdef HAVE_WS2TCPIP_H +/* getaddrinfo for Windows CE (and Windows). */ +#include <ws2tcpip.h> +#endif + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#ifndef PROCESS_QUERY_LIMITED_INFORMATION +/* MinGW32 < 4 does not define this value in its headers */ +#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000) +#endif + +typedef int socklen_t; + +/* uncomment to enable windows event based poll implementation */ +//#define USE_CHRIS_IMPL + +void +_dbus_win_set_errno (int err) +{ +#ifdef DBUS_WINCE + SetLastError (err); +#else + errno = err; +#endif +} + +static BOOL is_winxp_sp3_or_lower (void); + +/* + * _MIB_TCPROW_EX and friends are not available in system headers + * and are mapped to attribute identical ...OWNER_PID typedefs. + */ +typedef MIB_TCPROW_OWNER_PID _MIB_TCPROW_EX; +typedef MIB_TCPTABLE_OWNER_PID MIB_TCPTABLE_EX; +typedef PMIB_TCPTABLE_OWNER_PID PMIB_TCPTABLE_EX; +typedef DWORD (WINAPI *ProcAllocateAndGetTcpExtTableFromStack)(PMIB_TCPTABLE_EX*,BOOL,HANDLE,DWORD,DWORD); + +/* Not protected by a lock, but if we miss a write, all that + * happens is that the lazy initialization will happen in two threads + * concurrently - it results in the same value either way so that's OK */ +static ProcAllocateAndGetTcpExtTableFromStack lpfnAllocateAndGetTcpExTableFromStack = NULL; + +/** + * AllocateAndGetTcpExTableFromStack() is undocumented and not exported, + * but is the only way to do this in older XP versions. + * @return true if the procedures could be loaded + */ +static BOOL +load_ex_ip_helper_procedures(void) +{ + HMODULE hModule = LoadLibrary ("iphlpapi.dll"); + if (hModule == NULL) + { + _dbus_verbose ("could not load iphlpapi.dll\n"); + return FALSE; + } + + lpfnAllocateAndGetTcpExTableFromStack = (ProcAllocateAndGetTcpExtTableFromStack) (void (*)(void))GetProcAddress (hModule, "AllocateAndGetTcpExTableFromStack"); + if (lpfnAllocateAndGetTcpExTableFromStack == NULL) + { + _dbus_verbose ("could not find function AllocateAndGetTcpExTableFromStack in iphlpapi.dll\n"); + return FALSE; + } + return TRUE; +} + +/** + * get pid from localhost tcp connection using peer_port + * This function is available on WinXP >= SP3 + * @param peer_port peers tcp port + * @return process id or 0 if connection has not been found + */ +static dbus_pid_t +get_pid_from_extended_tcp_table(int peer_port) +{ + dbus_pid_t result; + DWORD errorCode, size = 0, i; + MIB_TCPTABLE_OWNER_PID *tcp_table; + + if ((errorCode = + GetExtendedTcpTable (NULL, &size, TRUE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0)) == ERROR_INSUFFICIENT_BUFFER) + { + tcp_table = (MIB_TCPTABLE_OWNER_PID *) dbus_malloc (size); + if (tcp_table == NULL) + { + _dbus_verbose ("Error allocating memory\n"); + return 0; + } + } + else + { + _dbus_win_warn_win_error ("unexpected error returned from GetExtendedTcpTable", errorCode); + return 0; + } + + if ((errorCode = GetExtendedTcpTable (tcp_table, &size, TRUE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0)) != NO_ERROR) + { + _dbus_verbose ("Error fetching tcp table %d\n", (int)errorCode); + dbus_free (tcp_table); + return 0; + } + + result = 0; + for (i = 0; i < tcp_table->dwNumEntries; i++) + { + MIB_TCPROW_OWNER_PID *p = &tcp_table->table[i]; + int local_address = ntohl (p->dwLocalAddr); + int local_port = ntohs (p->dwLocalPort); + if (p->dwState == MIB_TCP_STATE_ESTAB + && local_address == INADDR_LOOPBACK && local_port == peer_port) + result = p->dwOwningPid; + } + + dbus_free (tcp_table); + _dbus_verbose ("got pid %lu\n", result); + return result; +} + +/** + * get pid from localhost tcp connection using peer_port + * This function is available on all WinXP versions, but + * not in wine (at least version <= 1.6.0) + * @param peer_port peers tcp port + * @return process id or 0 if connection has not been found + */ +static dbus_pid_t +get_pid_from_tcp_ex_table(int peer_port) +{ + dbus_pid_t result; + DWORD errorCode, i; + PMIB_TCPTABLE_EX tcp_table = NULL; + + if (!load_ex_ip_helper_procedures ()) + { + _dbus_verbose + ("Error not been able to load iphelper procedures\n"); + return 0; + } + + errorCode = lpfnAllocateAndGetTcpExTableFromStack (&tcp_table, TRUE, GetProcessHeap(), 0, 2); + + if (errorCode != NO_ERROR) + { + _dbus_verbose + ("Error not been able to call AllocateAndGetTcpExTableFromStack()\n"); + return 0; + } + + result = 0; + for (i = 0; i < tcp_table->dwNumEntries; i++) + { + _MIB_TCPROW_EX *p = &tcp_table->table[i]; + int local_port = ntohs (p->dwLocalPort); + int local_address = ntohl (p->dwLocalAddr); + if (local_address == INADDR_LOOPBACK && local_port == peer_port) + { + result = p->dwOwningPid; + break; + } + } + + HeapFree (GetProcessHeap(), 0, tcp_table); + _dbus_verbose ("got pid %lu\n", result); + return result; +} + +/** + * @brief return peer process id from tcp handle for localhost connections + * @param handle tcp socket descriptor + * @return process id or 0 in case the process id could not be fetched + */ +static dbus_pid_t +_dbus_get_peer_pid_from_tcp_handle (int handle) +{ + struct sockaddr_storage addr; + socklen_t len = sizeof (addr); + int peer_port; + + dbus_pid_t result; + dbus_bool_t is_localhost = FALSE; + + getpeername (handle, (struct sockaddr *) &addr, &len); + + if (addr.ss_family == AF_INET) + { + struct sockaddr_in *s = (struct sockaddr_in *) &addr; + peer_port = ntohs (s->sin_port); + is_localhost = (ntohl (s->sin_addr.s_addr) == INADDR_LOOPBACK); + } + else if (addr.ss_family == AF_INET6) + { + _dbus_verbose ("FIXME [61922]: IPV6 support not working on windows\n"); + return 0; + /* + struct sockaddr_in6 *s = (struct sockaddr_in6 * )&addr; + peer_port = ntohs (s->sin6_port); + is_localhost = (memcmp(s->sin6_addr.s6_addr, in6addr_loopback.s6_addr, 16) == 0); + _dbus_verbose ("IPV6 %08x %08x\n", s->sin6_addr.s6_addr, in6addr_loopback.s6_addr); + */ + } + else + { + _dbus_verbose ("no idea what address family %d is\n", addr.ss_family); + return 0; + } + + if (!is_localhost) + { + _dbus_verbose ("could not fetch process id from remote process\n"); + return 0; + } + + if (peer_port == 0) + { + _dbus_verbose + ("Error not been able to fetch tcp peer port from connection\n"); + return 0; + } + + _dbus_verbose ("trying to get peer's pid\n"); + + result = get_pid_from_extended_tcp_table (peer_port); + if (result > 0) + return result; + result = get_pid_from_tcp_ex_table (peer_port); + return result; +} + +/* Convert GetLastError() to a dbus error. */ +const char* +_dbus_win_error_from_last_error (void) +{ + switch (GetLastError()) + { + case 0: + return DBUS_ERROR_FAILED; + + case ERROR_NO_MORE_FILES: + case ERROR_TOO_MANY_OPEN_FILES: + return DBUS_ERROR_LIMITS_EXCEEDED; /* kernel out of memory */ + + case ERROR_ACCESS_DENIED: + case ERROR_CANNOT_MAKE: + return DBUS_ERROR_ACCESS_DENIED; + + case ERROR_NOT_ENOUGH_MEMORY: + return DBUS_ERROR_NO_MEMORY; + + case ERROR_FILE_EXISTS: + return DBUS_ERROR_FILE_EXISTS; + + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + return DBUS_ERROR_FILE_NOT_FOUND; + + default: + return DBUS_ERROR_FAILED; + } +} + + +char* +_dbus_win_error_string (int error_number) +{ + char *msg; + + FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, error_number, 0, + (LPSTR) &msg, 0, NULL); + + if (msg[strlen (msg) - 1] == '\n') + msg[strlen (msg) - 1] = '\0'; + if (msg[strlen (msg) - 1] == '\r') + msg[strlen (msg) - 1] = '\0'; + + return msg; +} + +void +_dbus_win_free_error_string (char *string) +{ + LocalFree (string); +} + +/** + * Socket interface + * + */ + +/** + * Thin wrapper around the read() system call that appends + * the data it reads to the DBusString buffer. It appends + * up to the given count, and returns the same value + * and same errno as read(). The only exception is that + * _dbus_read_socket() handles EINTR for you. + * _dbus_read_socket() can return ENOMEM, even though + * regular UNIX read doesn't. + * + * @param fd the file descriptor to read from + * @param buffer the buffer to append data to + * @param count the amount of data to read + * @returns the number of bytes read or -1 + */ + +int +_dbus_read_socket (DBusSocket fd, + DBusString *buffer, + int count) +{ + int bytes_read; + int start; + char *data; + + _dbus_assert (count >= 0); + + start = _dbus_string_get_length (buffer); + + if (!_dbus_string_lengthen (buffer, count)) + { + _dbus_win_set_errno (ENOMEM); + return -1; + } + + data = _dbus_string_get_data_len (buffer, start, count); + + again: + + _dbus_verbose ("recv: count=%d fd=%Iu\n", count, fd.sock); + bytes_read = recv (fd.sock, data, count, 0); + + if (bytes_read == SOCKET_ERROR) + { + DBUS_SOCKET_SET_ERRNO(); + _dbus_verbose ("recv: failed: %s (%d)\n", _dbus_strerror (errno), errno); + bytes_read = -1; + } + else + _dbus_verbose ("recv: = %d\n", bytes_read); + + if (bytes_read < 0) + { + if (errno == EINTR) + goto again; + else + { + /* put length back (note that this doesn't actually realloc anything) */ + _dbus_string_set_length (buffer, start); + return -1; + } + } + else + { + /* put length back (doesn't actually realloc) */ + _dbus_string_set_length (buffer, start + bytes_read); + +#if 0 + if (bytes_read > 0) + _dbus_verbose_bytes_of_string (buffer, start, bytes_read); +#endif + + return bytes_read; + } +} + +/** + * Thin wrapper around the write() system call that writes a part of a + * DBusString and handles EINTR for you. + * + * @param fd the file descriptor to write + * @param buffer the buffer to write data from + * @param start the first byte in the buffer to write + * @param len the number of bytes to try to write + * @returns the number of bytes written or -1 on error + */ +int +_dbus_write_socket (DBusSocket fd, + const DBusString *buffer, + int start, + int len) +{ + const char *data; + int bytes_written; + + data = _dbus_string_get_const_data_len (buffer, start, len); + + again: + + _dbus_verbose ("send: len=%d fd=%Iu\n", len, fd.sock); + bytes_written = send (fd.sock, data, len, 0); + + if (bytes_written == SOCKET_ERROR) + { + DBUS_SOCKET_SET_ERRNO(); + _dbus_verbose ("send: failed: %s\n", _dbus_strerror_from_errno ()); + bytes_written = -1; + } + else + _dbus_verbose ("send: = %d\n", bytes_written); + + if (bytes_written < 0 && errno == EINTR) + goto again; + +#if 0 + if (bytes_written > 0) + _dbus_verbose_bytes_of_string (buffer, start, bytes_written); +#endif + + return bytes_written; +} + + +/** + * Closes a socket and invalidates it. + * + * @param fd the file descriptor + * @param error error object + * @returns #FALSE if error set + */ +dbus_bool_t +_dbus_close_socket (DBusSocket *fd, + DBusError *error) +{ + _dbus_assert (fd != NULL); + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + again: + if (closesocket (fd->sock) == SOCKET_ERROR) + { + DBUS_SOCKET_SET_ERRNO (); + + if (errno == EINTR) + goto again; + + dbus_set_error (error, _dbus_error_from_errno (errno), + "Could not close socket: socket=%Iu, , %s", + fd->sock, _dbus_strerror_from_errno ()); + _dbus_socket_invalidate (fd); + return FALSE; + } + _dbus_verbose ("socket=%Iu, \n", fd->sock); + + _dbus_socket_invalidate (fd); + return TRUE; +} + +/** + * Sets the file descriptor to be close + * on exec. Should be called for all file + * descriptors in D-Bus code. + * + * @param handle the Windows HANDLE (a SOCKET is also OK) + */ +static void +_dbus_win_handle_set_close_on_exec (HANDLE handle) +{ + if ( !SetHandleInformation( (HANDLE) handle, + HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE, + 0 /*disable both flags*/ ) ) + { + _dbus_win_warn_win_error ("Disabling socket handle inheritance failed:", GetLastError()); + } +} + +/** + * Sets a file descriptor to be nonblocking. + * + * @param handle the file descriptor. + * @param error address of error location. + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_set_socket_nonblocking (DBusSocket handle, + DBusError *error) +{ + u_long one = 1; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (ioctlsocket (handle.sock, FIONBIO, &one) == SOCKET_ERROR) + { + DBUS_SOCKET_SET_ERRNO (); + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to set socket %Iu to nonblocking: %s", + handle.sock, _dbus_strerror_from_errno ()); + return FALSE; + } + + return TRUE; +} + + +/** + * Like _dbus_write() but will use writev() if possible + * to write both buffers in sequence. The return value + * is the number of bytes written in the first buffer, + * plus the number written in the second. If the first + * buffer is written successfully and an error occurs + * writing the second, the number of bytes in the first + * is returned (i.e. the error is ignored), on systems that + * don't have writev. Handles EINTR for you. + * The second buffer may be #NULL. + * + * @param fd the file descriptor + * @param buffer1 first buffer + * @param start1 first byte to write in first buffer + * @param len1 number of bytes to write from first buffer + * @param buffer2 second buffer, or #NULL + * @param start2 first byte to write in second buffer + * @param len2 number of bytes to write in second buffer + * @returns total bytes written from both buffers, or -1 on error + */ +int +_dbus_write_socket_two (DBusSocket fd, + const DBusString *buffer1, + int start1, + int len1, + const DBusString *buffer2, + int start2, + int len2) +{ + WSABUF vectors[2]; + const char *data1; + const char *data2; + int rc; + DWORD bytes_written; + + _dbus_assert (buffer1 != NULL); + _dbus_assert (start1 >= 0); + _dbus_assert (start2 >= 0); + _dbus_assert (len1 >= 0); + _dbus_assert (len2 >= 0); + + + data1 = _dbus_string_get_const_data_len (buffer1, start1, len1); + + if (buffer2 != NULL) + data2 = _dbus_string_get_const_data_len (buffer2, start2, len2); + else + { + data2 = NULL; + start2 = 0; + len2 = 0; + } + + vectors[0].buf = (char*) data1; + vectors[0].len = len1; + vectors[1].buf = (char*) data2; + vectors[1].len = len2; + + again: + + _dbus_verbose ("WSASend: len1+2=%d+%d fd=%Iu\n", len1, len2, fd.sock); + rc = WSASend (fd.sock, + vectors, + data2 ? 2 : 1, + &bytes_written, + 0, + NULL, + NULL); + + if (rc == SOCKET_ERROR) + { + DBUS_SOCKET_SET_ERRNO (); + _dbus_verbose ("WSASend: failed: %s\n", _dbus_strerror_from_errno ()); + bytes_written = (DWORD) -1; + } + else + _dbus_verbose ("WSASend: = %ld\n", bytes_written); + + if (bytes_written == (DWORD) -1 && errno == EINTR) + goto again; + + return bytes_written; +} + +#if 0 + +/** + * Opens the client side of a Windows named pipe. The connection D-BUS + * file descriptor index is returned. It is set up as nonblocking. + * + * @param path the path to named pipe socket + * @param error return location for error code + * @returns connection D-BUS file descriptor or -1 on error + */ +int +_dbus_connect_named_pipe (const char *path, + DBusError *error) +{ + _dbus_assert_not_reached ("not implemented"); +} + +#endif + +/** + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_win_startup_winsock (void) +{ + /* Straight from MSDN, deuglified */ + + /* Protected by _DBUS_LOCK_sysdeps */ + static dbus_bool_t beenhere = FALSE; + + WORD wVersionRequested; + WSADATA wsaData; + int err; + + if (!_DBUS_LOCK (sysdeps)) + return FALSE; + + if (beenhere) + goto out; + + wVersionRequested = MAKEWORD (2, 0); + + err = WSAStartup (wVersionRequested, &wsaData); + if (err != 0) + { + _dbus_assert_not_reached ("Could not initialize WinSock"); + _dbus_abort (); + } + + /* Confirm that the WinSock DLL supports 2.0. Note that if the DLL + * supports versions greater than 2.0 in addition to 2.0, it will + * still return 2.0 in wVersion since that is the version we + * requested. + */ + if (LOBYTE (wsaData.wVersion) != 2 || + HIBYTE (wsaData.wVersion) != 0) + { + _dbus_assert_not_reached ("No usable WinSock found"); + _dbus_abort (); + } + + beenhere = TRUE; + +out: + _DBUS_UNLOCK (sysdeps); + return TRUE; +} + + + + + + + + + +/************************************************************************ + + UTF / string code + + ************************************************************************/ + +/** + * Measure the message length without terminating nul + */ +int _dbus_printf_string_upper_bound (const char *format, + va_list args) +{ + /* MSVCRT's vsnprintf semantics are a bit different */ + char buf[1024]; + int bufsize; + int len; + va_list args_copy; + + bufsize = sizeof (buf); + va_copy (args_copy, args); + len = _vsnprintf (buf, bufsize - 1, format, args_copy); + va_end (args_copy); + + while (len == -1) /* try again */ + { + char *p; + + bufsize *= 2; + + p = malloc (bufsize); + + if (p == NULL) + return -1; + + va_copy (args_copy, args); + len = _vsnprintf (p, bufsize - 1, format, args_copy); + va_end (args_copy); + free (p); + } + + return len; +} + + +/** + * Returns the UTF-16 form of a UTF-8 string. The result should be + * freed with dbus_free() when no longer needed. + * + * @param str the UTF-8 string + * @param error return location for error code + */ +wchar_t * +_dbus_win_utf8_to_utf16 (const char *str, + DBusError *error) +{ + DBusString s; + int n; + wchar_t *retval; + + _dbus_string_init_const (&s, str); + + if (!_dbus_string_validate_utf8 (&s, 0, _dbus_string_get_length (&s))) + { + dbus_set_error_const (error, DBUS_ERROR_FAILED, "Invalid UTF-8"); + return NULL; + } + + n = MultiByteToWideChar (CP_UTF8, 0, str, -1, NULL, 0); + + if (n == 0) + { + _dbus_win_set_error_from_win_error (error, GetLastError ()); + return NULL; + } + + retval = dbus_new (wchar_t, n); + + if (!retval) + { + _DBUS_SET_OOM (error); + return NULL; + } + + if (MultiByteToWideChar (CP_UTF8, 0, str, -1, retval, n) != n) + { + dbus_free (retval); + dbus_set_error_const (error, DBUS_ERROR_FAILED, "MultiByteToWideChar inconsistency"); + return NULL; + } + + return retval; +} + +/** + * Returns the UTF-8 form of a UTF-16 string. The result should be + * freed with dbus_free() when no longer needed. + * + * @param str the UTF-16 string + * @param error return location for error code + */ +char * +_dbus_win_utf16_to_utf8 (const wchar_t *str, + DBusError *error) +{ + int n; + char *retval; + + n = WideCharToMultiByte (CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); + + if (n == 0) + { + _dbus_win_set_error_from_win_error (error, GetLastError ()); + return NULL; + } + + retval = dbus_malloc (n); + + if (!retval) + { + _DBUS_SET_OOM (error); + return NULL; + } + + if (WideCharToMultiByte (CP_UTF8, 0, str, -1, retval, n, NULL, NULL) != n) + { + dbus_free (retval); + dbus_set_error_const (error, DBUS_ERROR_FAILED, "WideCharToMultiByte inconsistency"); + return NULL; + } + + return retval; +} + + + + + + +/************************************************************************ + + + ************************************************************************/ + +dbus_bool_t +_dbus_win_account_to_sid (const wchar_t *waccount, + void **ppsid, + DBusError *error) +{ + dbus_bool_t retval = FALSE; + DWORD sid_length, wdomain_length; + SID_NAME_USE use; + wchar_t *wdomain; + + *ppsid = NULL; + + sid_length = 0; + wdomain_length = 0; + if (!LookupAccountNameW (NULL, waccount, NULL, &sid_length, + NULL, &wdomain_length, &use) && + GetLastError () != ERROR_INSUFFICIENT_BUFFER) + { + _dbus_win_set_error_from_win_error (error, GetLastError ()); + return FALSE; + } + + *ppsid = dbus_malloc (sid_length); + if (!*ppsid) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + wdomain = dbus_new (wchar_t, wdomain_length); + if (!wdomain) + { + _DBUS_SET_OOM (error); + goto out1; + } + + if (!LookupAccountNameW (NULL, waccount, (PSID) *ppsid, &sid_length, + wdomain, &wdomain_length, &use)) + { + _dbus_win_set_error_from_win_error (error, GetLastError ()); + goto out2; + } + + if (!IsValidSid ((PSID) *ppsid)) + { + dbus_set_error_const (error, DBUS_ERROR_FAILED, "Invalid SID"); + goto out2; + } + + retval = TRUE; + +out2: + dbus_free (wdomain); +out1: + if (!retval) + { + dbus_free (*ppsid); + *ppsid = NULL; + } + + return retval; +} + +/** @} end of sysdeps-win */ + + +/** + * The only reason this is separate from _dbus_getpid() is to allow it + * on Windows for logging but not for other purposes. + * + * @returns process ID to put in log messages + */ +unsigned long +_dbus_pid_for_log (void) +{ + return _dbus_getpid (); +} + +#ifndef DBUS_WINCE + +static BOOL +is_winxp_sp3_or_lower (void) +{ + OSVERSIONINFOEX osvi; + DWORDLONG dwlConditionMask = 0; + int op=VER_LESS_EQUAL; + + // Initialize the OSVERSIONINFOEX structure. + + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + osvi.dwMajorVersion = 5; + osvi.dwMinorVersion = 1; + osvi.wServicePackMajor = 3; + osvi.wServicePackMinor = 0; + + // Initialize the condition mask. + + VER_SET_CONDITION (dwlConditionMask, VER_MAJORVERSION, op); + VER_SET_CONDITION (dwlConditionMask, VER_MINORVERSION, op); + VER_SET_CONDITION (dwlConditionMask, VER_SERVICEPACKMAJOR, op); + VER_SET_CONDITION (dwlConditionMask, VER_SERVICEPACKMINOR, op); + + // Perform the test. + + return VerifyVersionInfo( + &osvi, + VER_MAJORVERSION | VER_MINORVERSION | + VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + dwlConditionMask); +} + +/** Gets our SID + * @param sid points to sid buffer, need to be freed with LocalFree() + * @param process_id the process id for which the sid should be returned (use 0 for current process) + * @returns process sid + */ +dbus_bool_t +_dbus_getsid(char **sid, dbus_pid_t process_id) +{ + HANDLE process_token = INVALID_HANDLE_VALUE; + TOKEN_USER *token_user = NULL; + DWORD n; + PSID psid; + int retval = FALSE; + + HANDLE process_handle; + if (process_id == 0) + process_handle = GetCurrentProcess(); + else if (is_winxp_sp3_or_lower()) + process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process_id); + else + process_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id); + + if (!OpenProcessToken (process_handle, TOKEN_QUERY, &process_token)) + { + _dbus_win_warn_win_error ("OpenProcessToken failed", GetLastError ()); + goto failed; + } + if ((!GetTokenInformation (process_token, TokenUser, NULL, 0, &n) + && GetLastError () != ERROR_INSUFFICIENT_BUFFER) + || (token_user = alloca (n)) == NULL + || !GetTokenInformation (process_token, TokenUser, token_user, n, &n)) + { + _dbus_win_warn_win_error ("GetTokenInformation failed", GetLastError ()); + goto failed; + } + psid = token_user->User.Sid; + if (!IsValidSid (psid)) + { + _dbus_verbose("invalid sid\n"); + goto failed; + } + if (!ConvertSidToStringSidA (psid, sid)) + { + _dbus_verbose("invalid sid\n"); + goto failed; + } +//okay: + retval = TRUE; + +failed: + CloseHandle (process_handle); + if (process_token != INVALID_HANDLE_VALUE) + CloseHandle (process_token); + + _dbus_verbose("_dbus_getsid() got '%s' and returns %d\n", *sid, retval); + return retval; +} +#endif + +/************************************************************************ + + pipes + + ************************************************************************/ + +/** + * Creates pair of connect sockets (as in socketpair()). + * Sets both ends of the pair nonblocking. + * + * Marks both file descriptors as close-on-exec + * + * @param fd1 return location for one end + * @param fd2 return location for the other end + * @param blocking #TRUE if pair should be blocking + * @param error error return + * @returns #FALSE on failure (if error is set) + */ +dbus_bool_t +_dbus_socketpair (DBusSocket *fd1, + DBusSocket *fd2, + dbus_bool_t blocking, + DBusError *error) +{ + SOCKET temp, socket1 = -1, socket2 = -1; + struct sockaddr_in saddr; + int len; + u_long arg; + + if (!_dbus_win_startup_winsock ()) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + temp = socket (AF_INET, SOCK_STREAM, 0); + if (temp == INVALID_SOCKET) + { + DBUS_SOCKET_SET_ERRNO (); + goto out0; + } + + _DBUS_ZERO (saddr); + saddr.sin_family = AF_INET; + saddr.sin_port = 0; + saddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + if (bind (temp, (struct sockaddr *)&saddr, sizeof (saddr)) == SOCKET_ERROR) + { + DBUS_SOCKET_SET_ERRNO (); + goto out0; + } + + if (listen (temp, 1) == SOCKET_ERROR) + { + DBUS_SOCKET_SET_ERRNO (); + goto out0; + } + + len = sizeof (saddr); + if (getsockname (temp, (struct sockaddr *)&saddr, &len) == SOCKET_ERROR) + { + DBUS_SOCKET_SET_ERRNO (); + goto out0; + } + + socket1 = socket (AF_INET, SOCK_STREAM, 0); + if (socket1 == INVALID_SOCKET) + { + DBUS_SOCKET_SET_ERRNO (); + goto out0; + } + + if (connect (socket1, (struct sockaddr *)&saddr, len) == SOCKET_ERROR) + { + DBUS_SOCKET_SET_ERRNO (); + goto out1; + } + + socket2 = accept (temp, (struct sockaddr *) &saddr, &len); + if (socket2 == INVALID_SOCKET) + { + DBUS_SOCKET_SET_ERRNO (); + goto out1; + } + + if (!blocking) + { + arg = 1; + if (ioctlsocket (socket1, FIONBIO, &arg) == SOCKET_ERROR) + { + DBUS_SOCKET_SET_ERRNO (); + goto out2; + } + + arg = 1; + if (ioctlsocket (socket2, FIONBIO, &arg) == SOCKET_ERROR) + { + DBUS_SOCKET_SET_ERRNO (); + goto out2; + } + } + + fd1->sock = socket1; + fd2->sock = socket2; + + _dbus_verbose ("full-duplex pipe %Iu:%Iu <-> %Iu:%Iu\n", + fd1->sock, socket1, fd2->sock, socket2); + + closesocket (temp); + + return TRUE; + +out2: + closesocket (socket2); +out1: + closesocket (socket1); +out0: + closesocket (temp); + + dbus_set_error (error, _dbus_error_from_errno (errno), + "Could not setup socket pair: %s", + _dbus_strerror_from_errno ()); + + return FALSE; +} + +#ifdef DBUS_ENABLE_VERBOSE_MODE +static dbus_bool_t +_dbus_dump_fd_events (DBusPollFD *fds, int n_fds) +{ + DBusString msg = _DBUS_STRING_INIT_INVALID; + dbus_bool_t result = FALSE; + int i; + + if (!_dbus_string_init (&msg)) + goto oom; + + for (i = 0; i < n_fds; i++) + { + DBusPollFD *fdp = &fds[i]; + if (!_dbus_string_append (&msg, i > 0 ? "\n\t" : "\t")) + goto oom; + + if ((fdp->events & _DBUS_POLLIN) && + !_dbus_string_append_printf (&msg, "R:%Iu ", fdp->fd.sock)) + goto oom; + + if ((fdp->events & _DBUS_POLLOUT) && + !_dbus_string_append_printf (&msg, "W:%Iu ", fdp->fd.sock)) + goto oom; + + if (!_dbus_string_append_printf (&msg, "E:%Iu", fdp->fd.sock)) + goto oom; + } + + _dbus_verbose ("%s\n", _dbus_string_get_const_data (&msg)); + result = TRUE; +oom: + _dbus_string_free (&msg); + return result; +} + +#ifdef USE_CHRIS_IMPL +static dbus_bool_t +_dbus_dump_fd_revents (DBusPollFD *fds, int n_fds) +{ + DBusString msg = _DBUS_STRING_INIT_INVALID; + dbus_bool_t result = FALSE; + int i; + + if (!_dbus_string_init (&msg)) + goto oom; + + for (i = 0; i < n_fds; i++) + { + DBusPollFD *fdp = &fds[i]; + if (!_dbus_string_append (&msg, i > 0 ? "\n\t" : "\t")) + goto oom; + + if ((fdp->revents & _DBUS_POLLIN) && + !_dbus_string_append_printf (&msg, "R:%Iu ", fdp->fd.sock)) + goto oom; + + if ((fdp->revents & _DBUS_POLLOUT) && + !_dbus_string_append_printf (&msg, "W:%Iu ", fdp->fd.sock)) + goto oom; + + if ((fdp->revents & _DBUS_POLLERR) && + !_dbus_string_append_printf (&msg, "E:%Iu", fdp->fd.sock)) + goto oom; + } + + _dbus_verbose ("%s\n", _dbus_string_get_const_data (&msg)); + result = TRUE; +oom: + _dbus_string_free (&msg); + return result; +} +#else +static dbus_bool_t +_dbus_dump_fdset (DBusPollFD *fds, int n_fds, fd_set *read_set, fd_set *write_set, fd_set *err_set) +{ + DBusString msg = _DBUS_STRING_INIT_INVALID; + dbus_bool_t result = FALSE; + int i; + + if (!_dbus_string_init (&msg)) + goto oom; + + for (i = 0; i < n_fds; i++) + { + DBusPollFD *fdp = &fds[i]; + + if (!_dbus_string_append (&msg, i > 0 ? "\n\t" : "\t")) + goto oom; + + if (FD_ISSET (fdp->fd.sock, read_set) && + !_dbus_string_append_printf (&msg, "R:%Iu ", fdp->fd.sock)) + goto oom; + + if (FD_ISSET (fdp->fd.sock, write_set) && + !_dbus_string_append_printf (&msg, "W:%Iu ", fdp->fd.sock)) + goto oom; + + if (FD_ISSET (fdp->fd.sock, err_set) && + !_dbus_string_append_printf (&msg, "E:%Iu", fdp->fd.sock)) + goto oom; + } + _dbus_verbose ("%s\n", _dbus_string_get_const_data (&msg)); + result = TRUE; +oom: + _dbus_string_free (&msg); + return result; +} +#endif +#endif + +#ifdef USE_CHRIS_IMPL +/** + * Windows event based implementation for _dbus_poll(). + * + * @param fds the file descriptors to poll + * @param n_fds number of descriptors in the array + * @param timeout_milliseconds timeout or -1 for infinite + * @returns numbers of fds with revents, or <0 on error + */ +static int +_dbus_poll_events (DBusPollFD *fds, + int n_fds, + int timeout_milliseconds) +{ + int ret = 0; + int i; + DWORD ready; + +#define DBUS_STACK_WSAEVENTS 256 + WSAEVENT eventsOnStack[DBUS_STACK_WSAEVENTS]; + WSAEVENT *pEvents = NULL; + if (n_fds > DBUS_STACK_WSAEVENTS) + pEvents = calloc(sizeof(WSAEVENT), n_fds); + else + pEvents = eventsOnStack; + + if (pEvents == NULL) + { + _dbus_win_set_errno (ENOMEM); + ret = -1; + goto oom; + } + +#ifdef DBUS_ENABLE_VERBOSE_MODE + _dbus_verbose ("_dbus_poll: to=%d", timeout_milliseconds); + if (!_dbus_dump_fd_events (fds, n_fds)) + { + _dbus_win_set_errno (ENOMEM); + ret = -1; + goto oom; + } +#endif + + for (i = 0; i < n_fds; i++) + pEvents[i] = WSA_INVALID_EVENT; + + for (i = 0; i < n_fds; i++) + { + DBusPollFD *fdp = &fds[i]; + WSAEVENT ev; + long lNetworkEvents = FD_OOB; + + ev = WSACreateEvent(); + + if (fdp->events & _DBUS_POLLIN) + lNetworkEvents |= FD_READ | FD_ACCEPT | FD_CLOSE; + + if (fdp->events & _DBUS_POLLOUT) + lNetworkEvents |= FD_WRITE | FD_CONNECT; + + WSAEventSelect (fdp->fd.sock, ev, lNetworkEvents); + + pEvents[i] = ev; + } + + ready = WSAWaitForMultipleEvents (n_fds, pEvents, FALSE, timeout_milliseconds, FALSE); + + if (ready == WSA_WAIT_FAILED) + { + DBUS_SOCKET_SET_ERRNO (); + if (errno != WSAEWOULDBLOCK) + _dbus_verbose ("WSAWaitForMultipleEvents: failed: %s\n", _dbus_strerror_from_errno ()); + ret = -1; + } + else if (ready == WSA_WAIT_TIMEOUT) + { + _dbus_verbose ("WSAWaitForMultipleEvents: WSA_WAIT_TIMEOUT\n"); + ret = 0; + } + else if (ready < (WSA_WAIT_EVENT_0 + n_fds)) + { + for (i = 0; i < n_fds; i++) + { + DBusPollFD *fdp = &fds[i]; + WSANETWORKEVENTS ne; + + fdp->revents = 0; + + WSAEnumNetworkEvents (fdp->fd.sock, pEvents[i], &ne); + + if (ne.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) + fdp->revents |= _DBUS_POLLIN; + + if (ne.lNetworkEvents & (FD_WRITE | FD_CONNECT)) + fdp->revents |= _DBUS_POLLOUT; + + if (ne.lNetworkEvents & (FD_OOB)) + fdp->revents |= _DBUS_POLLERR; + + if(ne.lNetworkEvents) + ret++; + + WSAEventSelect (fdp->fd.sock, pEvents[i], 0); + } +#ifdef DBUS_ENABLE_VERBOSE_MODE + _dbus_verbose ("_dbus_poll: to=%d", timeout_milliseconds); + if (!_dbus_dump_fd_revents (fds, n_fds)) + { + _dbus_win_set_errno (ENOMEM); + ret = -1; + goto oom; + } +#endif + } + else + { + _dbus_verbose ("WSAWaitForMultipleEvents: failed for unknown reason!"); + ret = -1; + } + +oom: + if (pEvents != NULL) + { + for (i = 0; i < n_fds; i++) + { + if (pEvents[i] != WSA_INVALID_EVENT) + WSACloseEvent (pEvents[i]); + } + if (n_fds > DBUS_STACK_WSAEVENTS) + free (pEvents); + } + + return ret; +} +#else +/** + * Select based implementation for _dbus_poll(). + * + * @param fds the file descriptors to poll + * @param n_fds number of descriptors in the array + * @param timeout_milliseconds timeout or -1 for infinite + * @returns numbers of fds with revents, or <0 on error + */ +static int +_dbus_poll_select (DBusPollFD *fds, + int n_fds, + int timeout_milliseconds) +{ + fd_set read_set, write_set, err_set; + SOCKET max_fd = 0; + int i; + struct timeval tv; + int ready; + + FD_ZERO (&read_set); + FD_ZERO (&write_set); + FD_ZERO (&err_set); +#ifdef DBUS_ENABLE_VERBOSE_MODE + _dbus_verbose("_dbus_poll: to=%d\n", timeout_milliseconds); + if (!_dbus_dump_fd_events (fds, n_fds)) + return -1; +#endif + + for (i = 0; i < n_fds; i++) + { + DBusPollFD *fdp = &fds[i]; + + if (fdp->events & _DBUS_POLLIN) + FD_SET (fdp->fd.sock, &read_set); + + if (fdp->events & _DBUS_POLLOUT) + FD_SET (fdp->fd.sock, &write_set); + + FD_SET (fdp->fd.sock, &err_set); + + max_fd = MAX (max_fd, fdp->fd.sock); + } + + // Avoid random lockups with send(), for lack of a better solution so far + tv.tv_sec = timeout_milliseconds < 0 ? 1 : timeout_milliseconds / 1000; + tv.tv_usec = timeout_milliseconds < 0 ? 0 : (timeout_milliseconds % 1000) * 1000; + + ready = select (max_fd + 1, &read_set, &write_set, &err_set, &tv); + + if (DBUS_SOCKET_API_RETURNS_ERROR (ready)) + { + DBUS_SOCKET_SET_ERRNO (); + if (errno != WSAEWOULDBLOCK) + _dbus_verbose ("select: failed: %s\n", _dbus_strerror_from_errno ()); + } + else if (ready == 0) + _dbus_verbose ("select: = 0\n"); + else + if (ready > 0) + { +#ifdef DBUS_ENABLE_VERBOSE_MODE + _dbus_verbose ("select: to=%d\n", ready); + if (!_dbus_dump_fdset (fds, n_fds, &read_set, &write_set, &err_set)) + { + _dbus_win_set_errno (ENOMEM); + return -1; + } +#endif + for (i = 0; i < n_fds; i++) + { + DBusPollFD *fdp = &fds[i]; + + fdp->revents = 0; + + if (FD_ISSET (fdp->fd.sock, &read_set)) + fdp->revents |= _DBUS_POLLIN; + + if (FD_ISSET (fdp->fd.sock, &write_set)) + fdp->revents |= _DBUS_POLLOUT; + + if (FD_ISSET (fdp->fd.sock, &err_set)) + fdp->revents |= _DBUS_POLLERR; + } + } + return ready; +} +#endif + +/** + * Wrapper for poll(). + * + * @param fds the file descriptors to poll + * @param n_fds number of descriptors in the array + * @param timeout_milliseconds timeout or -1 for infinite + * @returns numbers of fds with revents, or <0 on error + */ +int +_dbus_poll (DBusPollFD *fds, + int n_fds, + int timeout_milliseconds) +{ +#ifdef USE_CHRIS_IMPL + return _dbus_poll_events (fds, n_fds, timeout_milliseconds); +#else + return _dbus_poll_select (fds, n_fds, timeout_milliseconds); +#endif +} + +/****************************************************************************** + +Original CVS version of dbus-sysdeps.c + +******************************************************************************/ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-sysdeps.c Wrappers around system/libc features (internal to D-Bus implementation) + * + * Copyright (C) 2002, 2003 Red Hat, Inc. + * Copyright (C) 2003 CodeFactory AB + * Copyright (C) 2005 Novell, Inc. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/** + * Exit the process, returning the given value. + * + * @param code the exit code + */ +void +_dbus_exit (int code) +{ + _exit (code); +} + +/** + * Creates a socket and connects to a socket at the given host + * and port. The connection fd is returned, and is set up as + * nonblocking. + * + * @param host the host name to connect to + * @param port the port to connect to + * @param family the address family to listen on, NULL for all + * @param error return location for error code + * @returns connection file descriptor or -1 on error + */ +DBusSocket +_dbus_connect_tcp_socket (const char *host, + const char *port, + const char *family, + DBusError *error) +{ + return _dbus_connect_tcp_socket_with_nonce (host, port, family, (const char*)NULL, error); +} + +DBusSocket +_dbus_connect_tcp_socket_with_nonce (const char *host, + const char *port, + const char *family, + const char *noncefile, + DBusError *error) +{ + int saved_errno = 0; + DBusList *connect_errors = NULL; + DBusSocket fd = DBUS_SOCKET_INIT; + int res; + struct addrinfo hints; + struct addrinfo *ai = NULL; + const struct addrinfo *tmp; + DBusError *connect_error; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (!_dbus_win_startup_winsock ()) + { + _DBUS_SET_OOM (error); + return _dbus_socket_get_invalid (); + } + + _DBUS_ZERO (hints); + + if (!family) + hints.ai_family = AF_UNSPEC; + else if (!strcmp(family, "ipv4")) + hints.ai_family = AF_INET; + else if (!strcmp(family, "ipv6")) + hints.ai_family = AF_INET6; + else + { + dbus_set_error (error, + DBUS_ERROR_BAD_ADDRESS, + "Unknown address family %s", family); + return _dbus_socket_get_invalid (); + } + hints.ai_protocol = IPPROTO_TCP; + hints.ai_socktype = SOCK_STREAM; +#ifdef AI_ADDRCONFIG + hints.ai_flags = AI_ADDRCONFIG; +#else + hints.ai_flags = 0; +#endif + + if ((res = getaddrinfo(host, port, &hints, &ai)) != 0 || !ai) + { + dbus_set_error (error, + _dbus_error_from_errno (res), + "Failed to lookup host/port: \"%s:%s\": %s (%d)", + host, port, _dbus_strerror (res), res); + goto out; + } + + tmp = ai; + while (tmp) + { + if ((fd.sock = socket (tmp->ai_family, SOCK_STREAM, 0)) == INVALID_SOCKET) + { + saved_errno = _dbus_get_low_level_socket_errno (); + dbus_set_error (error, + _dbus_error_from_errno (saved_errno), + "Failed to open socket: %s", + _dbus_strerror (saved_errno)); + _dbus_assert (!_dbus_socket_is_valid (fd)); + goto out; + } + _DBUS_ASSERT_ERROR_IS_CLEAR(error); + + if (connect (fd.sock, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) == SOCKET_ERROR) + { + saved_errno = _dbus_get_low_level_socket_errno (); + _dbus_close_socket (&fd, NULL); + + connect_error = dbus_new0 (DBusError, 1); + + if (connect_error == NULL) + { + _DBUS_SET_OOM (error); + goto out; + } + + dbus_error_init (connect_error); + _dbus_set_error_with_inet_sockaddr (connect_error, + tmp->ai_addr, tmp->ai_addrlen, + "Failed to connect to socket", + saved_errno); + + if (!_dbus_list_append (&connect_errors, connect_error)) + { + dbus_error_free (connect_error); + dbus_free (connect_error); + _DBUS_SET_OOM (error); + goto out; + } + + tmp = tmp->ai_next; + continue; + } + + break; + } + + if (!_dbus_socket_is_valid (fd)) + { + _dbus_combine_tcp_errors (&connect_errors, "Failed to connect", + host, port, error); + goto out; + } + + if (noncefile != NULL) + { + DBusString noncefileStr; + dbus_bool_t ret; + _dbus_string_init_const (&noncefileStr, noncefile); + ret = _dbus_send_nonce (fd, &noncefileStr, error); + + if (!ret) + { + _dbus_close_socket (&fd, NULL); + goto out; + } + } + + /* Every SOCKET is also a HANDLE. */ + _dbus_win_handle_set_close_on_exec ((HANDLE) fd.sock); + + if (!_dbus_set_socket_nonblocking (fd, error)) + { + _dbus_close_socket (&fd, NULL); + goto out; + } + +out: + if (ai != NULL) + freeaddrinfo (ai); + + while ((connect_error = _dbus_list_pop_first (&connect_errors))) + { + dbus_error_free (connect_error); + dbus_free (connect_error); + } + + return fd; +} + +/** + * Creates a socket and binds it to the given path, then listens on + * the socket. The socket is set to be nonblocking. In case of port=0 + * a random free port is used and returned in the port parameter. + * If inaddr_any is specified, the hostname is ignored. + * + * @param host the host name to listen on + * @param port the port to listen on, if zero a free port will be used + * @param family the address family to listen on, NULL for all + * @param retport string to return the actual port listened on + * @param retfamily string to return the actual family listened on + * @param fds_p location to store returned file descriptors + * @param error return location for errors + * @returns the number of listening file descriptors or -1 on error + */ +int +_dbus_listen_tcp_socket (const char *host, + const char *port, + const char *family, + DBusString *retport, + const char **retfamily, + DBusSocket **fds_p, + DBusError *error) +{ + int saved_errno; + int nlisten_fd = 0, res, i, port_num = -1; + DBusList *bind_errors = NULL; + DBusError *bind_error = NULL; + DBusSocket *listen_fd = NULL; + struct addrinfo hints; + struct addrinfo *ai, *tmp; + dbus_bool_t have_ipv4 = FALSE; + dbus_bool_t have_ipv6 = FALSE; + + // On Vista, sockaddr_gen must be a sockaddr_in6, and not a sockaddr_in6_old + //That's required for family == IPv6(which is the default on Vista if family is not given) + //So we use our own union instead of sockaddr_gen: + + typedef union { + struct sockaddr Address; + struct sockaddr_in AddressIn; + struct sockaddr_in6 AddressIn6; + } mysockaddr_gen; + + *fds_p = NULL; + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (!_dbus_win_startup_winsock ()) + { + _DBUS_SET_OOM (error); + return -1; + } + + _DBUS_ZERO (hints); + + if (!family) + hints.ai_family = AF_UNSPEC; + else if (!strcmp(family, "ipv4")) + hints.ai_family = AF_INET; + else if (!strcmp(family, "ipv6")) + hints.ai_family = AF_INET6; + else + { + dbus_set_error (error, + DBUS_ERROR_INVALID_ARGS, + "Unknown address family %s", family); + return -1; + } + + hints.ai_protocol = IPPROTO_TCP; + hints.ai_socktype = SOCK_STREAM; +#ifdef AI_ADDRCONFIG + hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE; +#else + hints.ai_flags = AI_PASSIVE; +#endif + + redo_lookup_with_port: + ai = NULL; + if ((res = getaddrinfo(host, port, &hints, &ai)) != 0 || !ai) + { + dbus_set_error (error, + _dbus_error_from_errno (res), + "Failed to lookup host/port: \"%s:%s\": %s (%d)", + host ? host : "*", port, _dbus_strerror(res), res); + return -1; + } + + tmp = ai; + while (tmp) + { + const int reuseaddr = 1, tcp_nodelay_on = 1; + DBusSocket fd = DBUS_SOCKET_INIT, *newlisten_fd; + + if ((fd.sock = socket (tmp->ai_family, SOCK_STREAM, 0)) == INVALID_SOCKET) + { + saved_errno = _dbus_get_low_level_socket_errno (); + dbus_set_error (error, + _dbus_error_from_errno (saved_errno), + "Failed to open socket: %s", + _dbus_strerror (saved_errno)); + _dbus_assert (!_dbus_socket_is_valid (fd)); + goto failed; + } + _DBUS_ASSERT_ERROR_IS_CLEAR(error); + + if (setsockopt (fd.sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuseaddr, sizeof(reuseaddr)) == SOCKET_ERROR) + { + saved_errno = _dbus_get_low_level_socket_errno (); + _dbus_warn ("Failed to set socket option \"%s:%s\": %s", + host ? host : "*", port, _dbus_strerror (saved_errno)); + } + + /* Nagle's algorithm imposes a huge delay on the initial messages + going over TCP. */ + if (setsockopt (fd.sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&tcp_nodelay_on, sizeof (tcp_nodelay_on)) == SOCKET_ERROR) + { + saved_errno = _dbus_get_low_level_socket_errno (); + _dbus_warn ("Failed to set TCP_NODELAY socket option \"%s:%s\": %s", + host ? host : "*", port, _dbus_strerror (saved_errno)); + } + + if (bind (fd.sock, (struct sockaddr *) tmp->ai_addr, tmp->ai_addrlen) == SOCKET_ERROR) + { + saved_errno = _dbus_get_low_level_socket_errno (); + closesocket (fd.sock); + + /* + * We don't treat this as a fatal error, because there might be + * other addresses that we can listen on. In particular: + * + * - If saved_errno is WSAEADDRINUSE after we + * "goto redo_lookup_with_port" after binding a port on one of the + * possible addresses, we will try to bind that same port on + * every address, including the same address again for a second + * time, which will fail with WSAEADDRINUSE . + * + * - If saved_errno is WSAEADDRINUSE, it might be because binding to + * an IPv6 address implicitly binds to a corresponding IPv4 + * address or vice versa. + * + * - If saved_errno is WSAEADDRNOTAVAIL when we asked for family + * AF_UNSPEC, it might be because IPv6 is disabled for this + * particular interface. + */ + bind_error = dbus_new0 (DBusError, 1); + + if (bind_error == NULL) + { + _DBUS_SET_OOM (error); + goto failed; + } + + dbus_error_init (bind_error); + _dbus_set_error_with_inet_sockaddr (bind_error, tmp->ai_addr, tmp->ai_addrlen, + "Failed to bind socket", + saved_errno); + + if (!_dbus_list_append (&bind_errors, bind_error)) + { + dbus_error_free (bind_error); + dbus_free (bind_error); + _DBUS_SET_OOM (error); + goto failed; + } + + /* Try the next address, maybe it will work better */ + tmp = tmp->ai_next; + continue; + } + + if (listen (fd.sock, 30 /* backlog */) == SOCKET_ERROR) + { + saved_errno = _dbus_get_low_level_socket_errno (); + closesocket (fd.sock); + _dbus_set_error_with_inet_sockaddr (error, tmp->ai_addr, tmp->ai_addrlen, + "Failed to listen on socket", + saved_errno); + goto failed; + } + + newlisten_fd = dbus_realloc(listen_fd, sizeof(DBusSocket)*(nlisten_fd+1)); + if (!newlisten_fd) + { + closesocket (fd.sock); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, + "Failed to allocate file handle array"); + goto failed; + } + listen_fd = newlisten_fd; + listen_fd[nlisten_fd] = fd; + nlisten_fd++; + + if (tmp->ai_addr->sa_family == AF_INET) + have_ipv4 = TRUE; + else if (tmp->ai_addr->sa_family == AF_INET6) + have_ipv6 = TRUE; + + if (!_dbus_string_get_length(retport)) + { + /* If the user didn't specify a port, or used 0, then + the kernel chooses a port. After the first address + is bound to, we need to force all remaining addresses + to use the same port */ + if (!port || !strcmp(port, "0")) + { + mysockaddr_gen addr; + socklen_t addrlen = sizeof(addr); + char portbuf[NI_MAXSERV]; + + if (getsockname (fd.sock, &addr.Address, &addrlen) == SOCKET_ERROR || + (res = getnameinfo (&addr.Address, addrlen, NULL, 0, + portbuf, sizeof(portbuf), + NI_NUMERICSERV)) != 0) + { + saved_errno = _dbus_get_low_level_socket_errno (); + dbus_set_error (error, _dbus_error_from_errno (saved_errno), + "Failed to resolve port \"%s:%s\": %s", + host ? host : "*", port, _dbus_strerror (saved_errno)); + goto failed; + } + if (!_dbus_string_append(retport, portbuf)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed; + } + + /* Release current address list & redo lookup */ + port = _dbus_string_get_const_data(retport); + freeaddrinfo(ai); + goto redo_lookup_with_port; + } + else + { + if (!_dbus_string_append(retport, port)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed; + } + } + } + + tmp = tmp->ai_next; + } + freeaddrinfo(ai); + ai = NULL; + + if (!nlisten_fd) + { + _dbus_combine_tcp_errors (&bind_errors, "Failed to bind", host, port, error); + goto failed; + } + + if (have_ipv4 && !have_ipv6) + *retfamily = "ipv4"; + else if (!have_ipv4 && have_ipv6) + *retfamily = "ipv6"; + + sscanf(_dbus_string_get_const_data(retport), "%d", &port_num); + + for (i = 0 ; i < nlisten_fd ; i++) + { + _dbus_win_handle_set_close_on_exec ((HANDLE) listen_fd[i].sock); + if (!_dbus_set_socket_nonblocking (listen_fd[i], error)) + { + goto failed; + } + } + + *fds_p = listen_fd; + + /* This list might be non-empty even on success, because we might be + * ignoring WSAEADDRINUSE or WSAEADDRNOTAVAIL */ + while ((bind_error = _dbus_list_pop_first (&bind_errors))) + { + dbus_error_free (bind_error); + dbus_free (bind_error); + } + return nlisten_fd; + + failed: + if (ai) + freeaddrinfo(ai); + for (i = 0 ; i < nlisten_fd ; i++) + closesocket (listen_fd[i].sock); + + while ((bind_error = _dbus_list_pop_first (&bind_errors))) + { + dbus_error_free (bind_error); + dbus_free (bind_error); + } + + dbus_free(listen_fd); + return -1; +} + + +/** + * Accepts a connection on a listening socket. + * Handles EINTR for you. + * + * @param listen_fd the listen file descriptor + * @returns the connection fd of the client, or -1 on error + */ +DBusSocket +_dbus_accept (DBusSocket listen_fd) +{ + DBusSocket client_fd; + + retry: + client_fd.sock = accept (listen_fd.sock, NULL, NULL); + + if (!_dbus_socket_is_valid (client_fd)) + { + DBUS_SOCKET_SET_ERRNO (); + if (errno == EINTR) + goto retry; + } + + _dbus_verbose ("client fd %Iu accepted\n", client_fd.sock); + + return client_fd; +} + + + + +dbus_bool_t +_dbus_send_credentials_socket (DBusSocket handle, + DBusError *error) +{ +/* FIXME: for the session bus credentials shouldn't matter (?), but + * for the system bus they are presumably essential. A rough outline + * of a way to implement the credential transfer would be this: + * + * client waits to *read* a byte. + * + * server creates a named pipe with a random name, sends a byte + * contining its length, and its name. + * + * client reads the name, connects to it (using Win32 API). + * + * server waits for connection to the named pipe, then calls + * ImpersonateNamedPipeClient(), notes its now-current credentials, + * calls RevertToSelf(), closes its handles to the named pipe, and + * is done. (Maybe there is some other way to get the SID of a named + * pipe client without having to use impersonation?) + * + * client closes its handles and is done. + * + * Ralf: Why not sending credentials over the given this connection ? + * Using named pipes makes it impossible to be connected from a unix client. + * + */ + int bytes_written; + DBusString buf; + + _dbus_string_init_const_len (&buf, "\0", 1); +again: + bytes_written = _dbus_write_socket (handle, &buf, 0, 1 ); + + if (bytes_written < 0 && errno == EINTR) + goto again; + + if (bytes_written < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to write credentials byte: %s", + _dbus_strerror_from_errno ()); + return FALSE; + } + else if (bytes_written == 0) + { + dbus_set_error (error, DBUS_ERROR_IO_ERROR, + "wrote zero bytes writing credentials byte"); + return FALSE; + } + else + { + _dbus_assert (bytes_written == 1); + _dbus_verbose ("wrote 1 zero byte, credential sending isn't implemented yet\n"); + return TRUE; + } + return TRUE; +} + +#ifdef HAVE_AFUNIX_H +/* + * Returns false with no error set if the socket is non-AF_UNIX + * (contrary to our usual convention). + * + * Returns false with an error set on failure to identify it. + */ +static dbus_bool_t +_dbus_socket_is_af_unix (DBusSocket s, + DBusError *error) +{ + struct sockaddr_un saddr; + int len; + + len = sizeof (saddr); + if (getsockname (s.sock, (struct sockaddr *)&saddr, &len) == SOCKET_ERROR) + { + DBUS_SOCKET_SET_ERRNO (); + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to getsockname: %s", + _dbus_strerror_from_errno ()); + return FALSE; + } + + return saddr.sun_family == AF_UNIX; +} + +/** + * @brief return peer process id from Unix domain socket handle + * @param handle AF_UNIX socket descriptor + * @return process id or 0 in case the process id could not be fetched + */ +static dbus_pid_t +_dbus_get_peer_pid_from_uds_handle (int handle) +{ + DWORD pid, drc; + + if (WSAIoctl (handle, SIO_AF_UNIX_GETPEERPID, + NULL, 0U, + &pid, sizeof (pid), &drc, + NULL, NULL) == SOCKET_ERROR) + { + _dbus_verbose ("failed to get peer's pid\n"); + return 0; + } + + return pid; +} +#endif + +/** + * Reads a single byte which must be nul (an error occurs otherwise), + * and reads unix credentials if available. Fills in pid/uid/gid with + * -1 if no credentials are available. Return value indicates whether + * a byte was read, not whether we got valid credentials. On some + * systems, such as Linux, reading/writing the byte isn't actually + * required, but we do it anyway just to avoid multiple codepaths. + * + * Fails if no byte is available, so you must select() first. + * + * The point of the byte is that on some systems we have to + * use sendmsg()/recvmsg() to transmit credentials. + * + * @param handle the client file descriptor + * @param credentials struct to fill with credentials of client + * @param error location to store error code + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_read_credentials_socket (DBusSocket handle, + DBusCredentials *credentials, + DBusError *error) +{ + int bytes_read = 0; + DBusString buf; +#ifdef HAVE_AFUNIX_H + dbus_bool_t uds = FALSE; +#endif + + char *sid = NULL; + dbus_pid_t pid; + int retval = FALSE; + + // could fail due too OOM + if (_dbus_string_init (&buf)) + { + bytes_read = _dbus_read_socket (handle, &buf, 1 ); + + if (bytes_read > 0) + _dbus_verbose ("got one zero byte from server\n"); + + _dbus_string_free (&buf); + } + +#ifdef HAVE_AFUNIX_H + uds = _dbus_socket_is_af_unix (handle, error); + if (dbus_error_is_set (error)) + return FALSE; + + if (uds) + pid = _dbus_get_peer_pid_from_uds_handle (handle.sock); + else +#endif + pid = _dbus_get_peer_pid_from_tcp_handle (handle.sock); + if (pid == 0) + return TRUE; + + _dbus_credentials_add_pid (credentials, pid); + + if (_dbus_getsid (&sid, pid)) + { + if (!_dbus_credentials_add_windows_sid (credentials, sid)) + goto out; + } + + retval = TRUE; + +out: + if (sid) + LocalFree (sid); + + return retval; +} + +/** +* Checks to make sure the given directory is +* private to the user +* +* @param dir the name of the directory +* @param error error return +* @returns #FALSE on failure +**/ +dbus_bool_t +_dbus_check_dir_is_private_to_user (DBusString *dir, DBusError *error) +{ + /* TODO */ + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return TRUE; +} + + +/** + * Appends the given filename to the given directory. + * + * @todo it might be cute to collapse multiple '/' such as "foo//" + * concat "//bar" + * + * @param dir the directory name + * @param next_component the filename + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_concat_dir_and_file (DBusString *dir, + const DBusString *next_component) +{ + dbus_bool_t dir_ends_in_slash; + dbus_bool_t file_starts_with_slash; + + if (_dbus_string_get_length (dir) == 0 || + _dbus_string_get_length (next_component) == 0) + return TRUE; + + dir_ends_in_slash = + ('/' == _dbus_string_get_byte (dir, _dbus_string_get_length (dir) - 1) || + '\\' == _dbus_string_get_byte (dir, _dbus_string_get_length (dir) - 1)); + + file_starts_with_slash = + ('/' == _dbus_string_get_byte (next_component, 0) || + '\\' == _dbus_string_get_byte (next_component, 0)); + + if (dir_ends_in_slash && file_starts_with_slash) + { + _dbus_string_shorten (dir, 1); + } + else if (!(dir_ends_in_slash || file_starts_with_slash)) + { + if (!_dbus_string_append_byte (dir, '\\')) + return FALSE; + } + + return _dbus_string_copy (next_component, 0, dir, + _dbus_string_get_length (dir)); +} + +/*---------------- DBusCredentials ----------------------------------*/ + +/** + * Adds the credentials corresponding to the given username. + * + * @param credentials credentials to fill in + * @param username the username + * @returns #TRUE if the username existed and we got some credentials + */ +dbus_bool_t +_dbus_credentials_add_from_user (DBusCredentials *credentials, + const DBusString *username, + DBusCredentialsAddFlags flags, + DBusError *error) +{ + if (!_dbus_credentials_add_windows_sid (credentials, + _dbus_string_get_const_data (username))) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + return TRUE; +} + +/** + * Adds the credentials of the current process to the + * passed-in credentials object. + * + * @param credentials credentials to add to + * @returns #FALSE if no memory; does not properly roll back on failure, so only some credentials may have been added + */ + +dbus_bool_t +_dbus_credentials_add_from_current_process (DBusCredentials *credentials) +{ + dbus_bool_t retval = FALSE; + char *sid = NULL; + + if (!_dbus_getsid(&sid, _dbus_getpid())) + goto failed; + + if (!_dbus_credentials_add_pid (credentials, _dbus_getpid())) + goto failed; + + if (!_dbus_credentials_add_windows_sid (credentials,sid)) + goto failed; + + retval = TRUE; + goto end; +failed: + retval = FALSE; +end: + if (sid) + LocalFree(sid); + + return retval; +} + +/** + * Append to the string the identity we would like to have when we + * authenticate, on UNIX this is the current process UID and on + * Windows something else, probably a Windows SID string. No escaping + * is required, that is done in dbus-auth.c. The username here + * need not be anything human-readable, it can be the machine-readable + * form i.e. a user id. + * + * @param str the string to append to + * @returns #FALSE on no memory + * @todo to which class belongs this + */ +dbus_bool_t +_dbus_append_user_from_current_process (DBusString *str) +{ + dbus_bool_t retval = FALSE; + char *sid = NULL; + + if (!_dbus_getsid(&sid, _dbus_getpid())) + return FALSE; + + retval = _dbus_string_append (str,sid); + + LocalFree(sid); + return retval; +} + +/** + * Gets our process ID + * @returns process ID + */ +dbus_pid_t +_dbus_getpid (void) +{ + return GetCurrentProcessId (); +} + +/** Gets our Unix UID + * @returns on Windows, just DBUS_UID_UNSET + */ +dbus_uid_t +_dbus_getuid (void) +{ + return DBUS_UID_UNSET; +} + +/** nanoseconds in a second */ +#define NANOSECONDS_PER_SECOND 1000000000 +/** microseconds in a second */ +#define MICROSECONDS_PER_SECOND 1000000 +/** milliseconds in a second */ +#define MILLISECONDS_PER_SECOND 1000 +/** nanoseconds in a millisecond */ +#define NANOSECONDS_PER_MILLISECOND 1000000 +/** microseconds in a millisecond */ +#define MICROSECONDS_PER_MILLISECOND 1000 + +/** + * Sleeps the given number of milliseconds. + * @param milliseconds number of milliseconds + */ +void +_dbus_sleep_milliseconds (int milliseconds) +{ + Sleep (milliseconds); +} + + +/** + * Get current time, as in gettimeofday(). Never uses the monotonic + * clock. + * + * @param tv_sec return location for number of seconds + * @param tv_usec return location for number of microseconds + */ +void +_dbus_get_real_time (dbus_int64_t *tv_sec, + long *tv_usec) +{ + FILETIME ft; + dbus_uint64_t time64; + + GetSystemTimeAsFileTime (&ft); + + memcpy (&time64, &ft, sizeof (time64)); + + /* Convert from 100s of nanoseconds since 1601-01-01 + * to Unix epoch. Yes, this is Y2038 unsafe. + */ + time64 -= DBUS_INT64_CONSTANT (116444736000000000); + time64 /= 10; + + if (tv_sec) + *tv_sec = time64 / 1000000; + + if (tv_usec) + *tv_usec = time64 % 1000000; +} + +/** + * Get current time, as in gettimeofday(). Use the monotonic clock if + * available, to avoid problems when the system time changes. + * + * @param tv_sec return location for number of seconds + * @param tv_usec return location for number of microseconds + */ +void +_dbus_get_monotonic_time (dbus_int64_t *tv_sec, + long *tv_usec) +{ + /* no implementation yet, fall back to wall-clock time */ + _dbus_get_real_time (tv_sec, tv_usec); +} + +/** + * signal (SIGPIPE, SIG_IGN); + */ +void +_dbus_disable_sigpipe (void) +{ +} + +/** + * Creates a directory. Unlike _dbus_ensure_directory(), this only succeeds + * if the directory is genuinely newly-created. + * + * @param filename directory filename + * @param error initialized error object + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_create_directory (const DBusString *filename, + DBusError *error) +{ + const char *filename_c; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + filename_c = _dbus_string_get_const_data (filename); + + if (!CreateDirectoryA (filename_c, NULL)) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Failed to create directory %s: %s\n", + filename_c, _dbus_strerror_from_errno ()); + return FALSE; + } + else + return TRUE; +} + +/** + * Creates a directory; succeeds if the directory + * is created or already existed. + * + * @param filename directory filename + * @param error initialized error object + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_ensure_directory (const DBusString *filename, + DBusError *error) +{ + const char *filename_c; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + filename_c = _dbus_string_get_const_data (filename); + + if (!CreateDirectoryA (filename_c, NULL)) + { + if (GetLastError () == ERROR_ALREADY_EXISTS) + return TRUE; + + dbus_set_error (error, DBUS_ERROR_FAILED, + "Failed to create directory %s: %s\n", + filename_c, _dbus_strerror_from_errno ()); + return FALSE; + } + else + return TRUE; +} + + +/** + * Generates the given number of random bytes, + * using the best mechanism we can come up with. + * + * @param str the string + * @param n_bytes the number of random bytes to append to string + * @param error location to store reason for failure + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_generate_random_bytes (DBusString *str, + int n_bytes, + DBusError *error) +{ + int old_len; + unsigned char *p; + HCRYPTPROV hprov; + + old_len = _dbus_string_get_length (str); + + if (!_dbus_string_lengthen (str, n_bytes)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + p = _dbus_string_get_udata_len (str, old_len, n_bytes); + + if (!CryptAcquireContext (&hprov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!CryptGenRandom (hprov, n_bytes, p)) + { + _DBUS_SET_OOM (error); + CryptReleaseContext (hprov, 0); + return FALSE; + } + + CryptReleaseContext (hprov, 0); + + return TRUE; +} + +/** + * Gets the temporary files directory, using GetTempPath() + * + * @returns location of temp directory, or #NULL if no memory for locking + */ +const char* +_dbus_get_tmpdir(void) +{ + /* Protected by _DBUS_LOCK_sysdeps */ + static const char* tmpdir = NULL; + static char buf[1000]; + + if (!_DBUS_LOCK (sysdeps)) + return NULL; + + if (tmpdir == NULL) + { + unsigned char *last_slash; + unsigned char *p = (unsigned char *)buf; + + if (!GetTempPathA (sizeof (buf), buf)) + { + _dbus_warn ("GetTempPath failed"); + _dbus_abort (); + } + + /* Drop terminating backslash or slash */ + last_slash = _mbsrchr (p, '\\'); + if (last_slash > p && last_slash[1] == '\0') + last_slash[0] = '\0'; + last_slash = _mbsrchr (p, '/'); + if (last_slash > p && last_slash[1] == '\0') + last_slash[0] = '\0'; + + tmpdir = buf; + } + + _DBUS_UNLOCK (sysdeps); + + _dbus_assert(tmpdir != NULL); + + return tmpdir; +} + + +/** + * Deletes the given file. + * + * @param filename the filename + * @param error error location + * + * @returns #TRUE if unlink() succeeded + */ +dbus_bool_t +_dbus_delete_file (const DBusString *filename, + DBusError *error) +{ + const char *filename_c; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + filename_c = _dbus_string_get_const_data (filename); + + if (DeleteFileA (filename_c) == 0) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Failed to delete file %s: %s\n", + filename_c, _dbus_strerror_from_errno ()); + return FALSE; + } + else + return TRUE; +} + +static dbus_uint32_t fromAscii(char ascii) +{ + if(ascii >= '0' && ascii <= '9') + return ascii - '0'; + if(ascii >= 'A' && ascii <= 'F') + return ascii - 'A' + 10; + if(ascii >= 'a' && ascii <= 'f') + return ascii - 'a' + 10; + return 0; +} + +dbus_bool_t _dbus_read_local_machine_uuid (DBusGUID *machine_id, + dbus_bool_t create_if_not_found, + DBusError *error) +{ +#ifdef DBUS_WINCE + return TRUE; + // TODO +#else + HW_PROFILE_INFOA info; + char *lpc = &info.szHwProfileGuid[0]; + dbus_uint32_t u; + + // the hw-profile guid lives long enough + if(!GetCurrentHwProfileA(&info)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); // FIXME + return FALSE; + } + + // Form: {12340001-4980-1920-6788-123456789012} + lpc++; + // 12340001 + u = ((fromAscii(lpc[0]) << 0) | + (fromAscii(lpc[1]) << 4) | + (fromAscii(lpc[2]) << 8) | + (fromAscii(lpc[3]) << 12) | + (fromAscii(lpc[4]) << 16) | + (fromAscii(lpc[5]) << 20) | + (fromAscii(lpc[6]) << 24) | + (fromAscii(lpc[7]) << 28)); + machine_id->as_uint32s[0] = u; + + lpc += 9; + // 4980-1920 + u = ((fromAscii(lpc[0]) << 0) | + (fromAscii(lpc[1]) << 4) | + (fromAscii(lpc[2]) << 8) | + (fromAscii(lpc[3]) << 12) | + (fromAscii(lpc[5]) << 16) | + (fromAscii(lpc[6]) << 20) | + (fromAscii(lpc[7]) << 24) | + (fromAscii(lpc[8]) << 28)); + machine_id->as_uint32s[1] = u; + + lpc += 10; + // 6788-1234 + u = ((fromAscii(lpc[0]) << 0) | + (fromAscii(lpc[1]) << 4) | + (fromAscii(lpc[2]) << 8) | + (fromAscii(lpc[3]) << 12) | + (fromAscii(lpc[5]) << 16) | + (fromAscii(lpc[6]) << 20) | + (fromAscii(lpc[7]) << 24) | + (fromAscii(lpc[8]) << 28)); + machine_id->as_uint32s[2] = u; + + lpc += 9; + // 56789012 + u = ((fromAscii(lpc[0]) << 0) | + (fromAscii(lpc[1]) << 4) | + (fromAscii(lpc[2]) << 8) | + (fromAscii(lpc[3]) << 12) | + (fromAscii(lpc[4]) << 16) | + (fromAscii(lpc[5]) << 20) | + (fromAscii(lpc[6]) << 24) | + (fromAscii(lpc[7]) << 28)); + machine_id->as_uint32s[3] = u; +#endif + return TRUE; +} + +// for proper cleanup in dbus-daemon +static HANDLE hDBusDaemonMutex = NULL; +static HANDLE hDBusSharedMem = NULL; +// sync _dbus_daemon_publish_session_bus_address, _dbus_daemon_unpublish_session_bus_address and _dbus_daemon_already_runs +static const char *cUniqueDBusInitMutex = "UniqueDBusInitMutex"; +// sync _dbus_get_autolaunch_address +static const char *cDBusAutolaunchMutex = "DBusAutolaunchMutex"; +// mutex to determine if dbus-daemon is already started (per user) +static const char *cDBusDaemonMutex = "DBusDaemonMutex"; +// named shm for dbus adress info (per user) +static const char *cDBusDaemonAddressInfo = "DBusDaemonAddressInfo"; + +/* custom command line parameter for autolaunching daemon */ +static const char *autolaunch_custom_command_line_parameter = ""; + +/** + * Set command line parameters for the dbus daemon to start + * for an autolaunch session. + * + * The specified instance must be valid until the dbus-daemon + * is started. + * + * This function is not thread-safe, and can only be called from a + * single-threaded unit test. + * + * @param path string to use as command line parameter + */ +void _dbus_test_win_autolaunch_set_command_line_parameter (const char *path) +{ + autolaunch_custom_command_line_parameter = path; +} + +static HANDLE *autolaunch_handle_location; + +/** + * Set location where to store process handle of an autostarted server + * + * This function is not thread-safe, and can only be called from a + * single-threaded unit test. + * + * After using the handle it must be closed with @ref CloseHandle(). + * + * @param location Pointer where to store the handle + */ +void +_dbus_test_win_set_autolaunch_handle_location (HANDLE *location) +{ + autolaunch_handle_location = location; +} + +/** + * Return the hash of the installation root directory, which can be + * used to construct a per-installation-root scope for autolaunching + * + * If the installation root directory could not be + * determined, the returned length is set to zero. + * + * @param out initialized DBusString instance to return hash string + * @returns #FALSE on OOM, #TRUE if not OOM + */ +static dbus_bool_t +_dbus_get_install_root_as_hash (DBusString *out) +{ + DBusString install_path; + dbus_bool_t retval = FALSE; + _dbus_assert (out != NULL); + + if (!_dbus_string_init (&install_path)) + return FALSE; + + if (!_dbus_get_install_root (&install_path)) + goto out; + + /* the install path can't be determined */ + if (_dbus_string_get_length (&install_path) == 0) + { + _dbus_string_set_length (out, 0); + retval = TRUE; + goto out; + } + + _dbus_string_tolower_ascii (&install_path, 0, _dbus_string_get_length (&install_path)); + + if (!_dbus_sha_compute (&install_path, out)) + goto out; + + retval = TRUE; + +out: + _dbus_string_free (&install_path); + return retval; +} + +/** + * Build a name from \p basestring and \p scope, and append it to \p out + * + * The name will be suitable for naming Windows objects such as mutexes + * and shared memory segments that need to be unique for each distinct + * \p scope, but shared between clients with the same \p scope. + * + * If \p scope has one of the special values recognised in autolaunch: + * addresses on Windows, substitute a unique string based on the scope + * (the username or the hash of the installation path) instead of the + * literal scope itself. + * + * With the '*install-path' \p scope the returned length can be zero, + * indicating that the name could not be determined. + * + * @param out initialized DBusString instance to return bus address + * @returns #FALSE on OOM, #TRUE if not OOM + */ +static dbus_bool_t +_dbus_get_address_string (DBusString *out, const char *basestring, const char *scope) +{ + _dbus_assert (out != NULL); + + if (!scope || strlen (scope) == 0) + { + return _dbus_string_append (out, basestring); + } + else if (strcmp (scope, "*install-path") == 0 + // for 1.3 compatibility + || strcmp (scope, "install-path") == 0) + { + DBusString temp; + dbus_bool_t retval = FALSE; + + if (!_dbus_string_init (&temp)) + return FALSE; + + if (!_dbus_get_install_root_as_hash (&temp)) + goto out; + + if (_dbus_string_get_length (&temp) == 0) + { + _dbus_string_set_length (out, 0); + retval = TRUE; + goto out; + } + + if (!_dbus_string_append_printf (out, "%s-%s", basestring, _dbus_string_get_const_data (&temp))) + goto out; + + retval = TRUE; +out: + _dbus_string_free (&temp); + return retval; + } + else if (strcmp (scope, "*user") == 0) + { + char *sid = NULL; + dbus_bool_t retval; + + if (!_dbus_getsid (&sid, _dbus_getpid())) + return FALSE; + + retval = _dbus_string_append_printf (out, "%s-%s", basestring, sid); + + LocalFree(sid); + + return retval; + } + else /* strlen(scope) > 0 */ + { + return _dbus_string_append_printf (out, "%s-%s", basestring, scope); + } +} + +/** + * Return name of shared memory segment constructed from the autolaunch scope \p scope + * + * See @ref _dbus_get_address_string for further usage information. + * + * @param out initialized DBusString instance to return shared memory segment name + * @returns #FALSE on OOM, #TRUE if not OOM + */ +static dbus_bool_t +_dbus_get_shm_name (DBusString *out,const char *scope) +{ + return _dbus_get_address_string (out, cDBusDaemonAddressInfo, scope); +} + +/** + * Return mutex name for scope \p scope in \p out + * + * See @ref _dbus_get_address_string for further usage information. + * + * @param out initialized DBusString instance to return mutex name + * @param scope scope for the requested mutex name + * @returns #FALSE on OOM, #TRUE if not OOM + */ +static dbus_bool_t +_dbus_get_mutex_name (DBusString *out, const char *scope) +{ + return _dbus_get_address_string (out, cDBusDaemonMutex, scope); +} + +dbus_bool_t +_dbus_daemon_is_session_bus_address_published (const char *scope) +{ + DBusRMutex *lock = NULL; + DBusString mutex_name; + + if (!_dbus_string_init (&mutex_name)) + return FALSE; + + _dbus_verbose ("scope:%s\n", scope); + if (!_dbus_get_mutex_name (&mutex_name, scope) || + /* not determinable */ + _dbus_string_get_length (&mutex_name) == 0) + { + _dbus_string_free (&mutex_name); + return FALSE; + } + + if (hDBusDaemonMutex) + { + _dbus_verbose ("(scope:%s) -> yes\n", scope); + return TRUE; + } + lock = _dbus_win_rmutex_named_new (cUniqueDBusInitMutex); + if (!lock) + return FALSE; + + // sync _dbus_daemon_publish_session_bus_address, _dbus_daemon_unpublish_session_bus_address and _dbus_daemon_already_runs + _dbus_platform_rmutex_lock (lock); + + // we use CreateMutex instead of OpenMutex because of possible race conditions, + // see http://msdn.microsoft.com/en-us/library/ms684315%28VS.85%29.aspx + hDBusDaemonMutex = CreateMutexA (NULL, FALSE, _dbus_string_get_const_data(&mutex_name)); + + /* The client uses mutex ownership to detect a running server, so the server should do so too. + Fortunally the client deletes the mutex in the lock protected area, so checking presence + will work too. */ + + _dbus_platform_rmutex_unlock (lock); + _dbus_platform_rmutex_free (lock); + + _dbus_string_free (&mutex_name); + + if (hDBusDaemonMutex == NULL) + { + _dbus_verbose ("(scope:%s) -> no\n", scope); + return FALSE; + } + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + CloseHandle(hDBusDaemonMutex); + hDBusDaemonMutex = NULL; + _dbus_verbose ("(scope:%s) -> yes\n", scope); + return TRUE; + } + // mutex wasn't created before, so return false. + // We leave the mutex name allocated for later reusage + // in _dbus_daemon_publish_session_bus_address. + _dbus_verbose ("(scope:%s) -> no\n", scope); + return FALSE; +} + +dbus_bool_t +_dbus_daemon_publish_session_bus_address (const char* address, const char *scope) +{ + DBusRMutex *lock = NULL; + char *shared_addr = NULL; + DBusString shm_name = _DBUS_STRING_INIT_INVALID; + DBusString mutex_name; + dbus_uint64_t len; + dbus_bool_t retval = FALSE; + + _dbus_assert (address); + + if (!_dbus_string_init (&mutex_name)) + return FALSE; + + _dbus_verbose ("address:%s scope:%s\n", address, scope); + if (!_dbus_get_mutex_name (&mutex_name, scope) || + /* not determinable */ + _dbus_string_get_length (&mutex_name) == 0) + { + _dbus_string_free (&mutex_name); + return FALSE; + } + + // sync _dbus_daemon_publish_session_bus_address, _dbus_daemon_unpublish_session_bus_address and _dbus_daemon_already_runs + lock = _dbus_win_rmutex_named_new (cUniqueDBusInitMutex); + if (lock == NULL) + { + _dbus_string_free (&mutex_name); + return FALSE; + } + + _dbus_platform_rmutex_lock (lock); + + if (!hDBusDaemonMutex) + { + hDBusDaemonMutex = CreateMutexA (NULL, FALSE, _dbus_string_get_const_data(&mutex_name)); + } + _dbus_string_free (&mutex_name); + + // acquire the mutex + if (WaitForSingleObject (hDBusDaemonMutex, 10) != WAIT_OBJECT_0) + { + CloseHandle (hDBusDaemonMutex); + goto out; + } + + if (!_dbus_string_init (&shm_name)) + { + goto out; + } + + if (!_dbus_get_shm_name (&shm_name, scope) || + /* not determinable */ + _dbus_string_get_length (&shm_name) == 0) + { + goto out; + } + + // create shm + len = strlen (address) + 1; + + hDBusSharedMem = CreateFileMappingA ( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, + len >> 32, len & 0xffffffffu, + _dbus_string_get_const_data (&shm_name) ); + _dbus_assert (hDBusSharedMem); + + shared_addr = MapViewOfFile (hDBusSharedMem, FILE_MAP_WRITE, 0, 0, 0); + + _dbus_assert (shared_addr); + + strcpy(shared_addr, address); + + // cleanup + UnmapViewOfFile (shared_addr); + + _dbus_verbose ("published session bus address at %s\n",_dbus_string_get_const_data (&shm_name)); + retval = TRUE; + +out: + _dbus_platform_rmutex_unlock (lock); + _dbus_platform_rmutex_free (lock); + _dbus_string_free (&shm_name); + return retval; +} + +/** + * Clear the platform-specific centralized location where the session + * bus address is published. + * + * This must only be called if \ref DBusServer.published_address is #TRUE, + * which is be the case if and only if platform-specific code has published + * the address centrally. + * + * On Windows, this is implemented by closing a global shared memory segment. + * + * On Unix, the session bus address is not published in a centralized + * location by libdbus, so this function does nothing. The closest + * equivalent on Unix is that the session bus address is published by the + * dbus-launch tool, and unpublished automatically when the dbus-launch + * tool exits. + * @return NULL in case of error + */ +dbus_bool_t +_dbus_daemon_unpublish_session_bus_address (void) +{ + DBusRMutex *lock = NULL; + + _dbus_verbose ("\n"); + // sync _dbus_daemon_publish_session_bus_address, _dbus_daemon_unpublish_session_bus_address and _dbus_daemon_already_runs + lock = _dbus_win_rmutex_named_new (cUniqueDBusInitMutex); + if (lock == NULL) + return FALSE; + + _dbus_platform_rmutex_lock (lock); + + CloseHandle (hDBusSharedMem); + + hDBusSharedMem = NULL; + + ReleaseMutex (hDBusDaemonMutex); + + CloseHandle (hDBusDaemonMutex); + + hDBusDaemonMutex = NULL; + + _dbus_platform_rmutex_unlock (lock); + _dbus_platform_rmutex_free (lock); + return TRUE; +} + +/** + * Get server bus address from shared memory segment provided by running dbus-daemon + * + * @param address initialized DBusString instance to store the retrieved address + * @param shm_name the name of the shared memory segment + * @param wait if TRUE wait maximum 2 seconds for the presence of the shared memory segment + * @return #TRUE the bus address was fetched from the shared memory segment + * @return #FALSE error during execution + */ +static dbus_bool_t +_dbus_get_autolaunch_shm (DBusString *address, DBusString *shm_name, dbus_bool_t wait) +{ + HANDLE sharedMem = NULL; + char *shared_addr; + int i; + int max = 20; /* max 2 seconds */ + dbus_bool_t retval = FALSE; + + if (!wait) + max = 1; + + // read shm + for (i = 0; i < max; ++i) + { + // we know that dbus-daemon is available, so we wait until shm is available + sharedMem = OpenFileMappingA (FILE_MAP_READ, FALSE, _dbus_string_get_const_data (shm_name)); + if (sharedMem == 0) + Sleep (100); + if (sharedMem != 0) + break; + } + + if (sharedMem == 0) + return FALSE; + + shared_addr = MapViewOfFile (sharedMem, FILE_MAP_READ, 0, 0, 0); + + if (!shared_addr) + goto out; + + retval = _dbus_string_append (address, shared_addr); + + UnmapViewOfFile (shared_addr); + +out: + CloseHandle (sharedMem); + return retval; +} + +static dbus_bool_t +_dbus_daemon_already_runs (DBusString *address, DBusString *shm_name, const char *scope) +{ + DBusRMutex *lock = NULL; + HANDLE daemon; + DBusString mutex_name; + dbus_bool_t retval = FALSE; + + if (!_dbus_string_init (&mutex_name)) + return FALSE; + + if (!_dbus_get_mutex_name (&mutex_name, scope) || + /* not determinable */ + _dbus_string_get_length (&mutex_name) == 0) + { + _dbus_string_free (&mutex_name); + return FALSE; + } + + // sync _dbus_daemon_publish_session_bus_address, _dbus_daemon_unpublish_session_bus_address and _dbus_daemon_already_runs + lock = _dbus_win_rmutex_named_new (cUniqueDBusInitMutex); + if (lock == NULL) + return FALSE; + + _dbus_platform_rmutex_lock (lock); + + // do checks + daemon = CreateMutexA (NULL, FALSE, _dbus_string_get_const_data (&mutex_name)); + if (WaitForSingleObject (daemon, 10) != WAIT_TIMEOUT) + { + ReleaseMutex (daemon); + CloseHandle (daemon); + goto out; + } + + // read shm, wait max 2 seconds + retval = _dbus_get_autolaunch_shm (address, shm_name, TRUE); + + // cleanup + CloseHandle (daemon); + +out: + _dbus_platform_rmutex_unlock (lock); + _dbus_platform_rmutex_free (lock); + _dbus_string_free (&mutex_name); + + return retval; +} + +dbus_bool_t +_dbus_get_autolaunch_address (const char *scope, + DBusString *address, + DBusError *error) +{ + DBusRMutex *lock = NULL; + STARTUPINFOA si; + PROCESS_INFORMATION pi; + dbus_bool_t retval = FALSE; + LPSTR lpFile; + char dbus_exe_path[MAX_PATH]; + DBusString dbus_args = _DBUS_STRING_INIT_INVALID; + const char *daemon_name = DBUS_DAEMON_NAME ".exe"; + DBusString shm_name; + HANDLE ready_event_handle = NULL; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (!_dbus_string_init (&shm_name)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_get_shm_name (&shm_name, scope) || + /* not determinable */ + _dbus_string_get_length (&shm_name) == 0) + { + dbus_set_error_const (error, DBUS_ERROR_FAILED, "could not determine shm name"); + goto out; + } + + lock = _dbus_win_rmutex_named_new (cDBusAutolaunchMutex); + if (lock == NULL) + { + dbus_set_error (error, DBUS_ERROR_FAILED, "Could not lock '%s'", cDBusAutolaunchMutex); + _dbus_string_free (&shm_name); + return FALSE; + } + + _dbus_platform_rmutex_lock (lock); + + if (_dbus_daemon_already_runs (address, &shm_name, scope)) + { + _dbus_verbose ("found running dbus daemon for scope '%s' at %s\n", + scope ? scope : "", _dbus_string_get_const_data (&shm_name)); + retval = TRUE; + goto out; + } + + if (!SearchPathA (NULL, daemon_name, NULL, sizeof (dbus_exe_path), dbus_exe_path, &lpFile)) + { + // Look in directory containing dbus shared library + HMODULE hmod; + char dbus_module_path[MAX_PATH]; + DWORD rc; + + _dbus_verbose ("did not found dbus daemon executable on default search path, " + "trying path where dbus shared library is located"); + + hmod = _dbus_win_get_dll_hmodule (); + rc = GetModuleFileNameA (hmod, dbus_module_path, sizeof (dbus_module_path)); + if (rc <= 0) + { + dbus_set_error_const (error, DBUS_ERROR_FAILED, "could not retrieve dbus shared library file name"); + retval = FALSE; + goto out; + } + else + { + char *ext_idx = strrchr (dbus_module_path, '\\'); + if (ext_idx) + *ext_idx = '\0'; + if (!SearchPathA (dbus_module_path, daemon_name, NULL, sizeof (dbus_exe_path), dbus_exe_path, &lpFile)) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Could not find dbus-daemon executable. " + "Please add the path to %s to your PATH " + "environment variable or start the daemon manually", + daemon_name); + retval = FALSE; + goto out; + } + _dbus_verbose ("found dbus daemon executable at %s", dbus_module_path); + } + } + + // Create process + ZeroMemory (&si, sizeof (si)); + si.cb = sizeof (si); + ZeroMemory (&pi, sizeof (pi)); + + if (!_dbus_string_init (&dbus_args)) + { + dbus_set_error_const (error, DBUS_ERROR_NO_MEMORY, "Failed to initialize argument buffer"); + retval = FALSE; + goto out; + } + + if (!_dbus_string_append_printf (&dbus_args, "\"%s\" %s", dbus_exe_path, + autolaunch_custom_command_line_parameter ? autolaunch_custom_command_line_parameter : "--session")) + { + _DBUS_SET_OOM (error); + retval = FALSE; + goto out; + } + + ready_event_handle = _dbus_win_event_create_inheritable (error); + if (ready_event_handle == NULL) + goto out; + + _dbus_verbose ("Creating connection readiness event: handle=%p\n", ready_event_handle); + if (!_dbus_string_append_printf (&dbus_args, " \"--ready-event-handle=%p\"", ready_event_handle)) + { + _DBUS_SET_OOM (error); + goto out; + } + + _dbus_verbose ("Starting dbus daemon with args: '%s'\n", _dbus_string_get_const_data (&dbus_args)); + if (CreateProcessA (dbus_exe_path, _dbus_string_get_data (&dbus_args), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) + { + DWORD status; + HANDLE events[2]; + + CloseHandle (pi.hThread); + + _dbus_verbose ("Wait until dbus-daemon is ready for connections (event handle %p)\n", ready_event_handle); + + events[0] = ready_event_handle; + events[1] = pi.hProcess; + status = WaitForMultipleObjects (2, events, FALSE, 30000); + + switch (status) + { + case WAIT_OBJECT_0: + /* ready event signalled, everything is okay */ + retval = TRUE; + break; + + case WAIT_OBJECT_0 + 1: + /* dbus-daemon process has exited */ + dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED, "dbus-daemon exited before signalling ready"); + goto out; + + case WAIT_FAILED: + _dbus_win_set_error_from_last_error (error, "Unable to wait for server readiness (handle %p)", ready_event_handle); + goto out; + + case WAIT_TIMEOUT: + /* GetLastError() is not set */ + dbus_set_error (error, DBUS_ERROR_TIMEOUT, "Timed out waiting for server readiness or exit (handle %p)", ready_event_handle); + goto out; + + default: + /* GetLastError() is probably not set? */ + dbus_set_error (error, DBUS_ERROR_FAILED, "Unknown result '%lu' while waiting for server readiness (handle %p)", status, ready_event_handle); + goto out; + } + _dbus_verbose ("Got signal that dbus-daemon with process id '%ld' is ready for connections\n", GetProcessId (pi.hProcess)); + + if (autolaunch_handle_location != NULL) + { + *autolaunch_handle_location = pi.hProcess; + _dbus_verbose ("Returning process handle of started server (handle=%p)\n", pi.hProcess); + } + else + { + CloseHandle (pi.hProcess); + } + + /* do not wait for the appearance of shm, we can assume that it is present */ + retval = _dbus_get_autolaunch_shm (address, &shm_name, FALSE); + if (retval == FALSE) + dbus_set_error_const (error, DBUS_ERROR_FAILED, "Failed to get autolaunch address from launched dbus-daemon"); + } + else + { + dbus_set_error_const (error, DBUS_ERROR_FAILED, "Failed to launch dbus-daemon"); + retval = FALSE; + } + +out: + _DBUS_ASSERT_ERROR_XOR_BOOL (error, retval); + _dbus_platform_rmutex_unlock (lock); + _dbus_platform_rmutex_free (lock); + _dbus_string_free (&shm_name); + _dbus_string_free (&dbus_args); + if (ready_event_handle) + _dbus_win_event_free (ready_event_handle, NULL); + + _DBUS_ASSERT_ERROR_XOR_BOOL (error, retval); + return retval; +} + +/** Makes the file readable by every user in the system. + * + * @param filename the filename + * @param error error location + * @returns #TRUE if the file's permissions could be changed. + */ +dbus_bool_t +_dbus_make_file_world_readable(const DBusString *filename, + DBusError *error) +{ + // TODO + return TRUE; +} + +/** + * Atomically increments an integer + * + * @param atomic pointer to the integer to increment + * @returns the value before incrementing + * + */ +dbus_int32_t +_dbus_atomic_inc (DBusAtomic *atomic) +{ +#ifdef HAVE_STDATOMIC_H + /* Atomic version of "old = *atomic; *atomic += 1; return old" */ + return atomic_fetch_add (&atomic->value, 1); +#else + /* Atomic version of "*atomic += 1; return *atomic - 1" */ + return InterlockedIncrement (&atomic->value) - 1; +#endif +} + +/** + * Atomically decrement an integer + * + * @param atomic pointer to the integer to decrement + * @returns the value before decrementing + * + */ +dbus_int32_t +_dbus_atomic_dec (DBusAtomic *atomic) +{ +#ifdef HAVE_STDATOMIC_H + /* Atomic version of "old = *atomic; *atomic -= 1; return old" */ + return atomic_fetch_sub (&atomic->value, 1); +#else + /* Atomic version of "*atomic -= 1; return *atomic + 1" */ + return InterlockedDecrement (&atomic->value) + 1; +#endif +} + +/** + * Atomically get the value of an integer. It may change at any time + * thereafter, so this is mostly only useful for assertions. + * + * @param atomic pointer to the integer to get + * @returns the value at this moment + */ +dbus_int32_t +_dbus_atomic_get (DBusAtomic *atomic) +{ +#ifdef HAVE_STDATOMIC_H + /* Atomic version of "return *atomic" */ + return atomic_load (&atomic->value); +#else + /* In this situation, GLib issues a MemoryBarrier() and then returns + * atomic->value. However, mingw from mingw.org (not to be confused with + * mingw-w64 from mingw-w64.sf.net) does not have MemoryBarrier in its + * headers, so we have to get a memory barrier some other way. + * + * InterlockedIncrement is older, and is documented on MSDN to be a full + * memory barrier, so let's use that. + */ + long dummy = 0; + + InterlockedExchange (&dummy, 1); + + return atomic->value; +#endif +} + +/** + * Atomically set the value of an integer to 0. + * + * @param atomic pointer to the integer to set + */ +void +_dbus_atomic_set_zero (DBusAtomic *atomic) +{ +#ifdef HAVE_STDATOMIC_H + /* Atomic version of "*atomic = 0" */ + atomic_store (&atomic->value, 0); +#else + InterlockedExchange (&atomic->value, 0); +#endif +} + +/** + * Atomically set the value of an integer to something nonzero. + * + * @param atomic pointer to the integer to set + */ +void +_dbus_atomic_set_nonzero (DBusAtomic *atomic) +{ +#ifdef HAVE_STDATOMIC_H + /* Atomic version of "*atomic = 1" */ + atomic_store (&atomic->value, 1); +#else + InterlockedExchange (&atomic->value, 1); +#endif +} + +/** + * Called when the bus daemon is signaled to reload its configuration; any + * caches should be nuked. Of course any caches that need explicit reload + * are probably broken, but c'est la vie. + * + * + */ +void +_dbus_flush_caches (void) +{ +} + +/** + * See if errno is EAGAIN or EWOULDBLOCK (this has to be done differently + * for Winsock so is abstracted) + * + * @returns #TRUE if e == EAGAIN or e == EWOULDBLOCK + */ +dbus_bool_t +_dbus_get_is_errno_eagain_or_ewouldblock (int e) +{ + return e == WSAEWOULDBLOCK; +} + +/** + * Fill str with the absolute path of the D-Bus installation, or truncate str + * to zero length if we cannot determine it. + * + * @param str buffer for installation path + * @returns #FALSE on OOM, #TRUE if not OOM + */ +dbus_bool_t +_dbus_get_install_root (DBusString *str) +{ + /* this is just an initial guess */ + DWORD pathLength = MAX_PATH; + unsigned char *lastSlash; + unsigned char *prefix; + + do + { + /* allocate enough space for our best guess at the length */ + if (!_dbus_string_set_length (str, pathLength)) + { + _dbus_string_set_length (str, 0); + return FALSE; + } + + SetLastError (0); + pathLength = GetModuleFileNameA (_dbus_win_get_dll_hmodule (), + _dbus_string_get_data (str), _dbus_string_get_length (str)); + + if (pathLength == 0 || GetLastError () != 0) + { + /* failed, but not OOM */ + _dbus_string_set_length (str, 0); + return TRUE; + } + + /* if the return is strictly less than the buffer size, it has + * not been truncated, so we can continue */ + if (pathLength < (DWORD) _dbus_string_get_length (str)) + { + /* reduce the length to match what Windows filled in */ + if (!_dbus_string_set_length (str, pathLength)) + { + _dbus_string_set_length (str, 0); + return FALSE; + } + + break; + } + + /* else it may have been truncated; try with a larger buffer */ + pathLength *= 2; + } + while (TRUE); + + /* the rest of this function works by direct byte manipulation of the + * underlying buffer */ + prefix = _dbus_string_get_udata (str); + + lastSlash = _mbsrchr (prefix, '\\'); + if (lastSlash == NULL) { + /* failed, but not OOM */ + _dbus_string_set_length (str, 0); + return TRUE; + } + //cut off binary name + lastSlash[1] = 0; + + //cut possible "\\bin" + //this fails if we are in a double-byte system codepage and the + //folder's name happens to end with the *bytes* + //"\\bin"... (I.e. the second byte of some Han character and then + //the Latin "bin", but that is not likely I think... + if (lastSlash - prefix >= 4 && _mbsnicmp (lastSlash - 4, (const unsigned char *)"\\bin", 4) == 0) + lastSlash[-3] = 0; + else if (lastSlash - prefix >= 10 && _mbsnicmp (lastSlash - 10, (const unsigned char *)"\\bin\\debug", 10) == 0) + lastSlash[-9] = 0; + else if (lastSlash - prefix >= 12 && _mbsnicmp (lastSlash - 12, (const unsigned char *)"\\bin\\release", 12) == 0) + lastSlash[-11] = 0; + + /* fix up the length to match the byte-manipulation */ + _dbus_string_set_length (str, strlen ((char *) prefix)); + + return TRUE; +} + +/* See comment in dbus-sysdeps-unix.c */ +dbus_bool_t +_dbus_lookup_session_address (dbus_bool_t *supported, + DBusString *address, + DBusError *error) +{ + /* Probably fill this in with something based on COM? */ + *supported = FALSE; + return TRUE; +} + +/** + * Appends the directory in which a keyring for the given credentials + * should be stored. The credentials should have either a Windows or + * UNIX user in them. The directory should be an absolute path. + * + * On UNIX the directory is ~/.dbus-keyrings while on Windows it should probably + * be something else, since the dotfile convention is not normal on Windows. + * + * @param directory string to append directory to + * @param credentials credentials the directory should be for + * + * @returns #FALSE on no memory + */ +dbus_bool_t +_dbus_append_keyring_directory_for_credentials (DBusString *directory, + DBusCredentials *credentials) +{ + DBusString homedir; + DBusString dotdir; + const char *homepath; + const char *homedrive; + + _dbus_assert (credentials != NULL); + _dbus_assert (!_dbus_credentials_are_anonymous (credentials)); + + if (!_dbus_string_init (&homedir)) + return FALSE; + + homedrive = _dbus_getenv("HOMEDRIVE"); + if (homedrive != NULL && *homedrive != '\0') + { + _dbus_string_append(&homedir,homedrive); + } + + homepath = _dbus_getenv("HOMEPATH"); + if (homepath != NULL && *homepath != '\0') + { + _dbus_string_append(&homedir,homepath); + } + +#ifdef DBUS_ENABLE_EMBEDDED_TESTS + { + const char *override; + + override = _dbus_getenv ("DBUS_TEST_HOMEDIR"); + if (override != NULL && *override != '\0') + { + _dbus_string_set_length (&homedir, 0); + if (!_dbus_string_append (&homedir, override)) + goto failed; + + _dbus_verbose ("Using fake homedir for testing: %s\n", + _dbus_string_get_const_data (&homedir)); + } + else + { + /* Not strictly thread-safe, but if we fail at thread-safety here, + * the worst that will happen is some extra warnings. */ + static dbus_bool_t already_warned = FALSE; + if (!already_warned) + { + _dbus_warn ("Using your real home directory for testing, set DBUS_TEST_HOMEDIR to avoid"); + already_warned = TRUE; + } + } + } +#endif + +#ifdef DBUS_WINCE + /* It's not possible to create a .something directory in Windows CE + using the file explorer. */ +#define KEYRING_DIR "dbus-keyrings" +#else +#define KEYRING_DIR ".dbus-keyrings" +#endif + + _dbus_string_init_const (&dotdir, KEYRING_DIR); + if (!_dbus_concat_dir_and_file (&homedir, + &dotdir)) + goto failed; + + if (!_dbus_string_copy (&homedir, 0, + directory, _dbus_string_get_length (directory))) { + goto failed; + } + + _dbus_string_free (&homedir); + return TRUE; + + failed: + _dbus_string_free (&homedir); + return FALSE; +} + +/** Checks if a file exists +* +* @param file full path to the file +* @returns #TRUE if file exists +*/ +dbus_bool_t +_dbus_file_exists (const char *file) +{ + DWORD attributes = GetFileAttributesA (file); + + if (attributes != INVALID_FILE_ATTRIBUTES && GetLastError() != ERROR_PATH_NOT_FOUND) + return TRUE; + else + return FALSE; +} + +/** + * A wrapper around strerror() because some platforms + * may be lame and not have strerror(). + * + * @param error_number errno. + * @returns error description. + */ +const char* +_dbus_strerror (int error_number) +{ +#ifdef DBUS_WINCE + // TODO + return "unknown"; +#else + const char *msg; + + switch (error_number) + { + case WSAEINTR: + return "Interrupted function call"; + case WSAEACCES: + return "Permission denied"; + case WSAEFAULT: + return "Bad address"; + case WSAEINVAL: + return "Invalid argument"; + case WSAEMFILE: + return "Too many open files"; + case WSAEWOULDBLOCK: + return "Resource temporarily unavailable"; + case WSAEINPROGRESS: + return "Operation now in progress"; + case WSAEALREADY: + return "Operation already in progress"; + case WSAENOTSOCK: + return "Socket operation on nonsocket"; + case WSAEDESTADDRREQ: + return "Destination address required"; + case WSAEMSGSIZE: + return "Message too long"; + case WSAEPROTOTYPE: + return "Protocol wrong type for socket"; + case WSAENOPROTOOPT: + return "Bad protocol option"; + case WSAEPROTONOSUPPORT: + return "Protocol not supported"; + case WSAESOCKTNOSUPPORT: + return "Socket type not supported"; + case WSAEOPNOTSUPP: + return "Operation not supported"; + case WSAEPFNOSUPPORT: + return "Protocol family not supported"; + case WSAEAFNOSUPPORT: + return "Address family not supported by protocol family"; + case WSAEADDRINUSE: + return "Address already in use"; + case WSAEADDRNOTAVAIL: + return "Cannot assign requested address"; + case WSAENETDOWN: + return "Network is down"; + case WSAENETUNREACH: + return "Network is unreachable"; + case WSAENETRESET: + return "Network dropped connection on reset"; + case WSAECONNABORTED: + return "Software caused connection abort"; + case WSAECONNRESET: + return "Connection reset by peer"; + case WSAENOBUFS: + return "No buffer space available"; + case WSAEISCONN: + return "Socket is already connected"; + case WSAENOTCONN: + return "Socket is not connected"; + case WSAESHUTDOWN: + return "Cannot send after socket shutdown"; + case WSAETIMEDOUT: + return "Connection timed out"; + case WSAECONNREFUSED: + return "Connection refused"; + case WSAEHOSTDOWN: + return "Host is down"; + case WSAEHOSTUNREACH: + return "No route to host"; + case WSAEPROCLIM: + return "Too many processes"; + case WSAEDISCON: + return "Graceful shutdown in progress"; + case WSATYPE_NOT_FOUND: + return "Class type not found"; + case WSAHOST_NOT_FOUND: + return "Host not found"; + case WSATRY_AGAIN: + return "Nonauthoritative host not found"; + case WSANO_RECOVERY: + return "This is a nonrecoverable error"; + case WSANO_DATA: + return "Valid name, no data record of requested type"; + case WSA_INVALID_HANDLE: + return "Specified event object handle is invalid"; + case WSA_INVALID_PARAMETER: + return "One or more parameters are invalid"; + case WSA_IO_INCOMPLETE: + return "Overlapped I/O event object not in signaled state"; + case WSA_IO_PENDING: + return "Overlapped operations will complete later"; + case WSA_NOT_ENOUGH_MEMORY: + return "Insufficient memory available"; + case WSA_OPERATION_ABORTED: + return "Overlapped operation aborted"; +#ifdef WSAINVALIDPROCTABLE + + case WSAINVALIDPROCTABLE: + return "Invalid procedure table from service provider"; +#endif +#ifdef WSAINVALIDPROVIDER + + case WSAINVALIDPROVIDER: + return "Invalid service provider version number"; +#endif +#ifdef WSAPROVIDERFAILEDINIT + + case WSAPROVIDERFAILEDINIT: + return "Unable to initialize a service provider"; +#endif + + case WSASYSCALLFAILURE: + return "System call failure"; + + default: + msg = strerror (error_number); + + if (msg == NULL) + msg = "unknown"; + + return msg; + } +#endif //DBUS_WINCE +} + +/** + * Assigns an error name and message corresponding to a Win32 error + * code to a DBusError. Does nothing if error is #NULL. + * + * @param error the error. + * @param code the Win32 error code + */ +void +_dbus_win_set_error_from_win_error (DBusError *error, + int code) +{ + char *msg; + + /* As we want the English message, use the A API */ + FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, code, MAKELANGID (LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPSTR) &msg, 0, NULL); + if (msg) + { + dbus_set_error (error, "win32.error", "%s", msg); + LocalFree (msg); + } + else + dbus_set_error (error, "win32.error", "Unknown error code %d or FormatMessage failed", code); +} + +void +_dbus_win_warn_win_error (const char *message, + unsigned long code) +{ + DBusError error; + + dbus_error_init (&error); + _dbus_win_set_error_from_win_error (&error, code); + _dbus_warn ("%s: %s", message, error.message); + dbus_error_free (&error); +} + +/** + * Removes a directory; Directory must be empty + * + * @param filename directory filename + * @param error initialized error object + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_delete_directory (const DBusString *filename, + DBusError *error) +{ + const char *filename_c; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + filename_c = _dbus_string_get_const_data (filename); + + if (RemoveDirectoryA (filename_c) == 0) + { + char *emsg = _dbus_win_error_string (GetLastError ()); + dbus_set_error (error, _dbus_win_error_from_last_error (), + "Failed to remove directory %s: %s", + filename_c, emsg); + _dbus_win_free_error_string (emsg); + return FALSE; + } + + return TRUE; +} + +/** + * Checks whether the filename is an absolute path + * + * @param filename the filename + * @returns #TRUE if an absolute path + */ +dbus_bool_t +_dbus_path_is_absolute (const DBusString *filename) +{ + if (_dbus_string_get_length (filename) > 0) + return _dbus_string_get_byte (filename, 1) == ':' + || _dbus_string_get_byte (filename, 0) == '\\' + || _dbus_string_get_byte (filename, 0) == '/'; + else + return FALSE; +} + +dbus_bool_t +_dbus_check_setuid (void) +{ + return FALSE; +} + +int +_dbus_save_socket_errno (void) +{ + return errno; +} + +void +_dbus_restore_socket_errno (int saved_errno) +{ + _dbus_win_set_errno (saved_errno); +} + +static const char *log_tag = "dbus"; +static DBusLogFlags log_flags = DBUS_LOG_FLAGS_STDERR; + +/** + * Initialize the system log. + * + * The "tag" is not copied, and must remain valid for the entire lifetime of + * the process or until _dbus_init_system_log() is called again. In practice + * it will normally be a constant. + * + * @param tag the name of the executable (syslog tag) + * @param mode whether to log to stderr, the system log or both + */ +void +_dbus_init_system_log (const char *tag, + DBusLogFlags flags) +{ + /* We never want to turn off logging completely */ + _dbus_assert ( + (flags & (DBUS_LOG_FLAGS_STDERR | DBUS_LOG_FLAGS_SYSTEM_LOG)) != 0); + + log_tag = tag; + log_flags = flags; +} + +/** + * Log a message to the system log file (e.g. syslog on Unix). + * + * @param severity a severity value + * @param msg a printf-style format string + * @param args arguments for the format string + */ +void +_dbus_logv (DBusSystemLogSeverity severity, + const char *msg, + va_list args) +{ + const char *s = ""; + va_list tmp; + + switch(severity) + { + case DBUS_SYSTEM_LOG_INFO: s = "info"; break; + case DBUS_SYSTEM_LOG_WARNING: s = "warning"; break; + case DBUS_SYSTEM_LOG_SECURITY: s = "security"; break; + case DBUS_SYSTEM_LOG_ERROR: s = "error"; break; + default: _dbus_assert_not_reached ("invalid log severity"); + } + + if (log_flags & DBUS_LOG_FLAGS_SYSTEM_LOG) + { + DBusString out = _DBUS_STRING_INIT_INVALID; + const char *message = NULL; + va_copy (tmp, args); + + if (!_dbus_string_init (&out)) + goto out; + if (!_dbus_string_append_printf (&out, "%s: ", s)) + goto out; + if (!_dbus_string_append_printf_valist (&out, msg, tmp)) + goto out; + message = _dbus_string_get_const_data (&out); +out: + if (message != NULL) + { + OutputDebugStringA (message); + } + else + { + OutputDebugStringA ("Out of memory while formatting message: '''"); + OutputDebugStringA (msg); + OutputDebugStringA ("'''"); + } + + va_end (tmp); + _dbus_string_free (&out); + } + + if (log_flags & DBUS_LOG_FLAGS_STDERR) + { + va_copy (tmp, args); + fprintf (stderr, "%s[%lu]: %s: ", log_tag, _dbus_pid_for_log (), s); + vfprintf (stderr, msg, tmp); + fprintf (stderr, "\n"); + va_end (tmp); + } +} + +/* + * Return the low-level representation of a socket error, as used by + * cross-platform socket APIs like inet_ntop(), send() and recv(). This + * is the standard errno on Unix, but is WSAGetLastError() on Windows. + * + * Some libdbus internal functions copy this into errno, but with + * hindsight that was probably a design flaw. + */ +int +_dbus_get_low_level_socket_errno (void) +{ + return WSAGetLastError (); +} + +void +_dbus_win_set_error_from_last_error (DBusError *error, + const char *format, + ...) +{ + const char *name; + char *message = NULL; + + if (error == NULL) + return; + + /* make sure to do this first, in case subsequent library calls overwrite GetLastError() */ + name = _dbus_win_error_from_last_error (); + message = _dbus_win_error_string (GetLastError ()); + + if (format != NULL) + { + DBusString str; + va_list args; + dbus_bool_t retval; + + if (!_dbus_string_init (&str)) + { + _DBUS_SET_OOM (error); + goto out; + } + + va_start (args, format); + retval = _dbus_string_append_printf_valist (&str, format, args); + va_end (args); + if (!retval) + { + _DBUS_SET_OOM (error); + _dbus_string_free (&str); + goto out; + } + + dbus_set_error (error, name, "%s: %s", _dbus_string_get_const_data (&str), message); + _dbus_string_free (&str); + } + else + { + dbus_set_error (error, name, "%s", message); + } + +out: + if (message != NULL) + _dbus_win_free_error_string (message); + + _DBUS_ASSERT_ERROR_IS_SET (error); +} + +/** + * Creates a Windows event object and returns the corresponding handle + * + * The returned object is unnamed, is a manual-reset event object, + * is initially in the non-signalled state, and is inheritable by child + * processes. + * + * @param error the error to set + * @return handle for the created event + * @return #NULL if an error has occurred, the reason is returned in \p error + */ +HANDLE +_dbus_win_event_create_inheritable (DBusError *error) +{ + HANDLE handle; + + handle = CreateEvent (NULL, TRUE, FALSE, NULL); + if (handle == NULL) + { + _dbus_win_set_error_from_last_error (error, "Could not create event"); + return NULL; + } + else if (GetLastError () == ERROR_ALREADY_EXISTS) + { + _dbus_win_set_error_from_last_error (error, "Event already exists"); + return NULL; + } + + if (!SetHandleInformation (handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) + { + _dbus_win_set_error_from_last_error (error, "Could not set inheritance for event %p", handle); + CloseHandle (handle); + return NULL; + } + return handle; +} + +/** + * Set a Windows event to the signalled state + * + * @param handle the handle for the event to be set + * @return TRUE the event was set successfully + * @return FALSE an error has occurred, the reason is returned in \p error + */ +dbus_bool_t +_dbus_win_event_set (HANDLE handle, DBusError *error) +{ + _dbus_assert (handle != NULL); + + if (!SetEvent (handle)) + { + _dbus_win_set_error_from_last_error (error, "Could not trigger event (handle %p)", handle); + return FALSE; + } + return TRUE; +} + +/** + * Wait for a Windows event to enter the signalled state + * + * @param handle the handle for the event to wait for + * @param timeout the waiting time in milliseconds, or INFINITE to wait forever, + * or 0 to check immediately and not wait (polling) + * @param error the error to set + * @return TRUE the event was set successfully + * @return FALSE an error has occurred, the reason is returned in \p error + */ +dbus_bool_t +_dbus_win_event_wait (HANDLE handle, int timeout, DBusError *error) +{ + DWORD status; + + _dbus_assert (handle != NULL); + + status = WaitForSingleObject (handle, timeout); + switch (status) + { + case WAIT_OBJECT_0: + return TRUE; + + case WAIT_FAILED: + { + _dbus_win_set_error_from_last_error (error, "Unable to wait for event (handle %p)", handle); + return FALSE; + } + + case WAIT_TIMEOUT: + /* GetLastError() is not set */ + dbus_set_error (error, DBUS_ERROR_TIMEOUT, "Timed out waiting for event (handle %p)", handle); + return FALSE; + + default: + /* GetLastError() is probably not set? */ + dbus_set_error (error, DBUS_ERROR_FAILED, "Unknown result '%lu' while waiting for event (handle %p)", status, handle); + return FALSE; + } +} + +/** + * Delete a Windows event + * + * @param handle handle for the event to delete + * @param error the error to set (optional) + * @return TRUE the event has been deleted successfully or the handle is one of the special sentinel values #NULL or #INVALID_HANDLE_VALUE + * @return FALSE an error has occurred, the reason is returned in \p error if specified + */ +dbus_bool_t +_dbus_win_event_free (HANDLE handle, DBusError *error) +{ + if (handle == NULL || handle == INVALID_HANDLE_VALUE) + return TRUE; + + if (CloseHandle (handle)) + return TRUE; + + /* the handle may already be closed */ + if (GetLastError () == ERROR_INVALID_HANDLE) + return TRUE; + + _dbus_win_set_error_from_last_error (error, "Could not close event (handle %p)", handle); + return FALSE; +} + +#ifdef HAVE_AFUNIX_H +static dbus_bool_t +_dbus_open_socket (SOCKET *socket_p, + int domain, + int type, + int protocol, + DBusError *error) +{ + if (!_dbus_win_startup_winsock ()) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + *socket_p = socket (domain, type, protocol); + if (*socket_p == INVALID_SOCKET) + { + DBUS_SOCKET_SET_ERRNO (); + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to open socket: %s", + _dbus_strerror_from_errno ()); + return FALSE; + } + + _dbus_win_handle_set_close_on_exec ((HANDLE) *socket_p); + return TRUE; +} + +/** + * Opens a UNIX domain socket (as in the socket() call). + * Does not bind the socket. + * + * This will set CLOEXEC for the socket returned + * + * @param return location for socket descriptor + * @param error return location for an error + * @returns #FALSE if error is set + */ +static dbus_bool_t +_dbus_open_unix_socket (SOCKET *socket, + DBusError *error) +{ + return _dbus_open_socket (socket, AF_UNIX, SOCK_STREAM, 0, error); +} +#endif /* HAVE_AFUNIX_H */ + +/** + * Creates a socket and connects it to the UNIX domain socket at the + * given path. The socket is returned, and is set up as + * nonblocking. + * + * Abstract socket usage always fails. + * + * This will set FD_CLOEXEC for the socket returned. + * + * @param path the path to UNIX domain socket + * @param abstract #TRUE to use abstract namespace + * @param error return location for error code + * @returns a valid socket on success or an invalid socket on error + */ +DBusSocket +_dbus_connect_unix_socket (const char *path, + dbus_bool_t abstract, + DBusError *error) +{ + DBusSocket s = DBUS_SOCKET_INIT; + +#ifdef HAVE_AFUNIX_H + struct sockaddr_un addr; + size_t path_len; + + _DBUS_STATIC_ASSERT (sizeof (addr.sun_path) > _DBUS_MAX_SUN_PATH_LENGTH); + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + _dbus_verbose ("connecting to unix socket %s abstract=%d\n", + path, abstract); + + if (abstract) + { + dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, + "Failed to connect: UNIX abstract socket is not supported on this system"); + return s; + } + + path_len = strlen (path); + if (path_len > _DBUS_MAX_SUN_PATH_LENGTH) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "Failed to connect: socket name too long"); + return s; + } + + if (!_dbus_open_unix_socket (&s.sock, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + return s; + } + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + _DBUS_ZERO (addr); + addr.sun_family = AF_UNIX; + strncpy (addr.sun_path, path, sizeof (addr.sun_path) - 1); + + if (connect (s.sock, (struct sockaddr *) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0) + { + DBUS_SOCKET_SET_ERRNO (); + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Failed to connect to socket %s: %s", + path, _dbus_strerror (errno)); + + _dbus_close_socket (&s, NULL); + return s; + } + + if (!_dbus_set_socket_nonblocking (s, error)) + _dbus_close_socket (&s, NULL); + +#else + dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, + "Failed to connect: UNIX socket is not supported with this build"); +#endif + + return s; +} + +/** + * Creates a socket and binds it to the given path, + * then listens on the socket. The socket is + * set to be nonblocking. + * + * Abstract socket usage always fails. + * + * This will set CLOEXEC for the socket returned + * + * @param path the socket name + * @param abstract #TRUE to use abstract namespace + * @param error return location for errors + * @returns a valid socket on success or an invalid socket on error + */ +DBusSocket +_dbus_listen_unix_socket (const char *path, + dbus_bool_t abstract, + DBusError *error) +{ + DBusSocket s = DBUS_SOCKET_INIT; + +#ifdef HAVE_AFUNIX_H + struct sockaddr_un addr; + size_t path_len; + _DBUS_STATIC_ASSERT (sizeof (addr.sun_path) > _DBUS_MAX_SUN_PATH_LENGTH); + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + _dbus_verbose ("listening on unix socket %s abstract=%d\n", + path, abstract); + + if (abstract) + { + dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, + "Failed to listen: UNIX abstract socket is not supported on this system"); + return s; + } + + if (!_dbus_open_unix_socket (&s.sock, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + return s; + } + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + _DBUS_ZERO (addr); + addr.sun_family = AF_UNIX; + path_len = strlen (path); + + /* see related comment in dbus-sysdeps-unix.c */ + /* there is no S_ISSOCK on windows yet, so just unlink the path */ + unlink (path); + + if (path_len > _DBUS_MAX_SUN_PATH_LENGTH) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "Failed to listen: socket name too long"); + _dbus_close_socket (&s, NULL); + return s; + } + + strncpy (addr.sun_path, path, sizeof (addr.sun_path) - 1); + + if (bind (s.sock, (struct sockaddr *) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0) + { + DBUS_SOCKET_SET_ERRNO (); + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to bind socket \"%s\": %s", + path, _dbus_strerror (errno)); + _dbus_close_socket (&s, NULL); + return s; + } + + if (listen (s.sock, SOMAXCONN /* backlog */) < 0) + { + DBUS_SOCKET_SET_ERRNO (); + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to listen on socket \"%s\": %s", + path, _dbus_strerror (errno)); + _dbus_close_socket (&s, NULL); + return s; + } + + if (!_dbus_set_socket_nonblocking (s, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + _dbus_close_socket (&s, NULL); + return s; + } +#else + dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, + "Failed to listen: UNIX socket is not supported with this build"); +#endif + + return s; +} + +/** @} end of sysdeps-win */ +/* tests in dbus-sysdeps-util.c */ |