update epan/dissectors/pidl/drsuapi/drsuapi.idl from samba
[wireshark-sm.git] / wsutil / win32-utils.c
blob60c7378cc2c79b264bd2a44bb193e9e96668e4c4
1 /* win32-utils.c
2 * Win32 utility routines
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 2006 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
11 #include <config.h>
12 #include <wsutil/array.h>
14 #include "win32-utils.h"
16 #include <tchar.h>
17 #include <versionhelpers.h>
19 /* Quote the argument element if necessary, so that it will get
20 * reconstructed correctly in the C runtime startup code. Note that
21 * the unquoting algorithm in the C runtime is really weird, and
22 * rather different than what Unix shells do. See stdargv.c in the C
23 * runtime sources (in the Platform SDK, in src/crt).
25 * Stolen from GLib's protect_argv(), an internal routine that quotes
26 * string in an argument list so that they arguments will be handled
27 * correctly in the command-line string passed to CreateProcess()
28 * if that string is constructed by gluing those strings together.
30 char *
31 protect_arg (const char *argv)
33 char *new_arg;
34 const char *p = argv;
35 char *q;
36 int len = 0;
37 bool need_dblquotes = false;
39 while (*p) {
40 if (*p == ' ' || *p == '\t')
41 need_dblquotes = true;
42 else if (*p == '"')
43 len++;
44 else if (*p == '\\') {
45 const char *pp = p;
47 while (*pp == '\\')
48 pp++;
49 if (*pp == '"')
50 len++;
52 len++;
53 p++;
56 q = new_arg = g_malloc (len + (need_dblquotes ? 2 : 0) + 1);
57 p = argv;
59 if (need_dblquotes)
60 *q++ = '"';
62 while (*p) {
63 if (*p == '"')
64 *q++ = '\\';
65 else if (*p == '\\') {
66 const char *pp = p;
68 while (*pp == '\\')
69 pp++;
70 if (*pp == '"')
71 *q++ = '\\';
73 *q++ = *p;
74 p++;
77 if (need_dblquotes)
78 *q++ = '"';
79 *q++ = '\0';
81 return new_arg;
84 #define PIPE_STR "\\pipe\\"
86 bool
87 win32_is_pipe_name(const char *pipe_name)
89 char *pncopy, *pos;
90 /* Under Windows, named pipes _must_ have the form
91 * "\\<server>\pipe\<pipename>". <server> may be "." for localhost.
92 * https://learn.microsoft.com/en-us/windows/win32/ipc/pipe-names
94 pncopy = g_strdup(pipe_name);
95 if ((pos = strstr(pncopy, "\\\\")) == pncopy) {
96 pos = strchr(pncopy + 3, '\\');
97 if (pos && g_ascii_strncasecmp(pos, PIPE_STR, strlen(PIPE_STR)) != 0)
98 pos = NULL;
101 g_free(pncopy);
103 return (pos != NULL);
107 * Generate a UTF-8 string for a Windows error.
111 * We make the buffer at least this big, under the assumption that doing
112 * so will reduce the number of reallocations to do. (Otherwise, why
113 * did Microsoft bother supporting a minimum buffer size?)
115 #define ERRBUF_SIZE 128
116 const char *
117 win32strerror(DWORD error)
119 DWORD retval;
120 WCHAR *utf16_message;
121 char *utf8_message;
122 char *tempmsg;
123 const char *msg;
126 * XXX - what language ID to use?
128 * For UN*Xes, g_strerror() may or may not return localized strings.
130 * We currently don't have localized strings, except for GUI items,
131 * but we might want to do so. On the other hand, if most of these
132 * messages are going to be read by Wireshark developers, English
133 * might be a better choice, so the developer doesn't have to get
134 * the message translated if it's in a language they don't happen
135 * to understand. Then again, we're including the error number,
136 * so the developer can just look that up.
138 retval = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK,
139 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
140 (LPTSTR)&utf16_message, ERRBUF_SIZE, NULL);
141 if (retval == 0) {
142 /* Failed. */
143 tempmsg = ws_strdup_printf("Couldn't get error message for error (%lu) (because %lu)",
144 error, GetLastError());
145 msg = g_intern_string(tempmsg);
146 g_free(tempmsg);
147 return msg;
150 utf8_message = g_utf16_to_utf8(utf16_message, -1, NULL, NULL, NULL);
151 LocalFree(utf16_message);
152 if (utf8_message == NULL) {
153 /* Conversion failed. */
154 tempmsg = ws_strdup_printf("Couldn't convert error message for error to UTF-8 (%lu) (because %lu)",
155 error, GetLastError());
156 msg = g_intern_string(tempmsg);
157 g_free(tempmsg);
158 return msg;
160 tempmsg = ws_strdup_printf("%s (%lu)", utf8_message, error);
161 g_free(utf8_message);
162 msg = g_intern_string(tempmsg);
163 g_free(tempmsg);
164 return msg;
168 * Generate a string for a Win32 exception code.
170 const char *
171 win32strexception(DWORD exception)
173 static char errbuf[ERRBUF_SIZE+1];
174 static const struct exception_msg {
175 DWORD code;
176 char *msg;
177 } exceptions[] = {
178 { EXCEPTION_ACCESS_VIOLATION, "Access violation" },
179 { EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "Array bounds exceeded" },
180 { EXCEPTION_BREAKPOINT, "Breakpoint" },
181 { EXCEPTION_DATATYPE_MISALIGNMENT, "Data type misalignment" },
182 { EXCEPTION_FLT_DENORMAL_OPERAND, "Denormal floating-point operand" },
183 { EXCEPTION_FLT_DIVIDE_BY_ZERO, "Floating-point divide by zero" },
184 { EXCEPTION_FLT_INEXACT_RESULT, "Floating-point inexact result" },
185 { EXCEPTION_FLT_INVALID_OPERATION, "Invalid floating-point operation" },
186 { EXCEPTION_FLT_OVERFLOW, "Floating-point overflow" },
187 { EXCEPTION_FLT_STACK_CHECK, "Floating-point stack check" },
188 { EXCEPTION_FLT_UNDERFLOW, "Floating-point underflow" },
189 { EXCEPTION_GUARD_PAGE, "Guard page violation" },
190 { EXCEPTION_ILLEGAL_INSTRUCTION, "Illegal instruction" },
191 { EXCEPTION_IN_PAGE_ERROR, "Page-in error" },
192 { EXCEPTION_INT_DIVIDE_BY_ZERO, "Integer divide by zero" },
193 { EXCEPTION_INT_OVERFLOW, "Integer overflow" },
194 { EXCEPTION_INVALID_DISPOSITION, "Invalid disposition" },
195 { EXCEPTION_INVALID_HANDLE, "Invalid handle" },
196 { EXCEPTION_NONCONTINUABLE_EXCEPTION, "Non-continuable exception" },
197 { EXCEPTION_PRIV_INSTRUCTION, "Privileged instruction" },
198 { EXCEPTION_SINGLE_STEP, "Single-step complete" },
199 { EXCEPTION_STACK_OVERFLOW, "Stack overflow" },
200 { 0, NULL }
202 #define N_EXCEPTIONS array_length(exceptions)
204 for (size_t i = 0; i < N_EXCEPTIONS; i++) {
205 if (exceptions[i].code == exception)
206 return exceptions[i].msg;
208 snprintf(errbuf, sizeof errbuf, "Exception 0x%08lx", exception);
209 return errbuf;
212 // This appears to be the closest equivalent to SIGPIPE on Windows.
213 // https://devblogs.microsoft.com/oldnewthing/?p=2433
214 // https://stackoverflow.com/a/53214/82195
216 static void win32_kill_child_on_exit(HANDLE child_handle) {
217 static HANDLE cjo_handle = NULL;
218 if (!cjo_handle) {
219 cjo_handle = CreateJobObject(NULL, NULL);
221 if (!cjo_handle) {
222 ws_log(LOG_DOMAIN_CAPTURE, LOG_LEVEL_DEBUG, "Could not create child cleanup job object: %s",
223 win32strerror(GetLastError()));
224 return;
227 JOBOBJECT_EXTENDED_LIMIT_INFORMATION cjo_jel_info = { 0 };
228 cjo_jel_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
229 BOOL sijo_ret = SetInformationJobObject(cjo_handle, JobObjectExtendedLimitInformation,
230 &cjo_jel_info, sizeof(cjo_jel_info));
231 if (!sijo_ret) {
232 ws_log(LOG_DOMAIN_CAPTURE, LOG_LEVEL_DEBUG, "Could not set child cleanup limits: %s",
233 win32strerror(GetLastError()));
237 BOOL aptjo_ret = AssignProcessToJobObject(cjo_handle, child_handle);
238 if (!aptjo_ret) {
239 ws_log(LOG_DOMAIN_CAPTURE, LOG_LEVEL_DEBUG, "Could not assign child cleanup process: %s",
240 win32strerror(GetLastError()));
244 BOOL win32_create_process(const char *application_name, const char *command_line, LPSECURITY_ATTRIBUTES process_attributes, LPSECURITY_ATTRIBUTES thread_attributes, size_t n_inherit_handles, HANDLE *inherit_handles, DWORD creation_flags, LPVOID environment, const char *current_directory, LPSTARTUPINFO startup_info, LPPROCESS_INFORMATION process_information)
246 gunichar2 *wappname = NULL, *wcurrentdirectory = NULL;
247 gunichar2 *wcommandline = g_utf8_to_utf16(command_line, -1, NULL, NULL, NULL);
248 LPPROC_THREAD_ATTRIBUTE_LIST attribute_list = NULL;
249 STARTUPINFOEX startup_infoex;
250 size_t i;
251 // CREATE_SUSPENDED: Suspend the child so that we can cleanly call
252 // AssignProcessToJobObject.
253 DWORD wcreationflags = creation_flags|CREATE_SUSPENDED;
254 // CREATE_BREAKAWAY_FROM_JOB: The main application might be associated with a job,
255 // e.g. if we're running under "Run As", ConEmu, or Visual Studio. On Windows
256 // <= 7 our child process needs to break away from it so that we can cleanly
257 // call AssignProcessToJobObject on *our* job.
258 // Windows >= 8 supports nested jobs so this isn't necessary there.
259 // https://blogs.msdn.microsoft.com/winsdk/2014/09/22/job-object-insanity/
261 if (! IsWindowsVersionOrGreater(6, 2, 0)) { // Windows 8
262 wcreationflags |= CREATE_BREAKAWAY_FROM_JOB;
265 if (application_name) {
266 wappname = g_utf8_to_utf16(application_name, -1, NULL, NULL, NULL);
268 if (current_directory) {
269 wcurrentdirectory = g_utf8_to_utf16(current_directory, -1, NULL, NULL, NULL);
271 if (n_inherit_handles > 0) {
272 size_t attr_size = 0;
273 BOOL success;
274 success = InitializeProcThreadAttributeList(NULL, 1, 0, &attr_size);
275 if (success || (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
276 attribute_list = g_malloc(attr_size);
277 success = InitializeProcThreadAttributeList(attribute_list, 1, 0, &attr_size);
279 if (success && (attribute_list != NULL)) {
280 success = UpdateProcThreadAttribute(attribute_list, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
281 inherit_handles, n_inherit_handles * sizeof(HANDLE), NULL, NULL);
283 if (!success && (attribute_list != NULL)) {
284 DeleteProcThreadAttributeList(attribute_list);
285 g_free(attribute_list);
286 attribute_list = NULL;
289 memset(&startup_infoex, 0, sizeof(startup_infoex));
290 startup_infoex.StartupInfo = *startup_info;
291 startup_infoex.StartupInfo.cb = sizeof(startup_infoex);
292 startup_infoex.lpAttributeList = attribute_list;
293 wcreationflags |= EXTENDED_STARTUPINFO_PRESENT;
294 for (i = 0; i < n_inherit_handles; i++) {
295 SetHandleInformation(inherit_handles[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
297 BOOL cp_res = CreateProcess(wappname, wcommandline, process_attributes, thread_attributes,
298 (n_inherit_handles > 0) ? true : false, wcreationflags, environment, wcurrentdirectory,
299 &startup_infoex.StartupInfo, process_information);
300 /* While this function makes the created process inherit only the explicitly
301 * listed handles, there can be other functions (in 3rd party libraries)
302 * that create processes inheriting all inheritable handles. To minimize
303 * number of unwanted handle duplicates (handle duplicate can extend object
304 * lifetime, e.g. pipe write end) created that way clear the inherit flag.
306 for (i = 0; i < n_inherit_handles; i++) {
307 SetHandleInformation(inherit_handles[i], HANDLE_FLAG_INHERIT, 0);
309 if (cp_res) {
310 win32_kill_child_on_exit(process_information->hProcess);
311 ResumeThread(process_information->hThread);
313 // XXX Else try again if CREATE_BREAKAWAY_FROM_JOB and GetLastError() == ERROR_ACCESS_DENIED?
315 if (attribute_list) {
316 DeleteProcThreadAttributeList(attribute_list);
317 g_free(attribute_list);
319 g_free(wappname);
320 g_free(wcommandline);
321 g_free(wcurrentdirectory);
322 return cp_res;
326 * Editor modelines - https://www.wireshark.org/tools/modelines.html
328 * Local Variables:
329 * c-basic-offset: 4
330 * tab-width: 8
331 * indent-tabs-mode: nil
332 * End:
334 * ex: set shiftwidth=4 tabstop=8 expandtab:
335 * :indentSize=4:tabSize=8:noTabs=true: