1 /* win32-utils.c
2 * Win32 utility routines
3 *
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 2006 Gerald Combs
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include <config.h>
12
13 #include "win32-utils.h"
14
15 #include <tchar.h>
16 #include <VersionHelpers.h>
17
18 #include <wsutil/wslog.h>
19
20 /* Quote the argument element if necessary, so that it will get
21 * reconstructed correctly in the C runtime startup code. Note that
22 * the unquoting algorithm in the C runtime is really weird, and
23 * rather different than what Unix shells do. See stdargv.c in the C
24 * runtime sources (in the Platform SDK, in src/crt).
25 *
26 * Stolen from GLib's protect_argv(), an internal routine that quotes
27 * string in an argument list so that they arguments will be handled
28 * correctly in the command-line string passed to CreateProcess()
29 * if that string is constructed by gluing those strings together.
30 */
31 gchar *
protect_arg(const gchar * argv)32 protect_arg (const gchar *argv)
33 {
34 gchar *new_arg;
35 const gchar *p = argv;
36 gchar *q;
37 gint len = 0;
38 gboolean need_dblquotes = FALSE;
39
40 while (*p) {
41 if (*p == ' ' || *p == '\t')
42 need_dblquotes = TRUE;
43 else if (*p == '"')
44 len++;
45 else if (*p == '\\') {
46 const gchar *pp = p;
47
48 while (*pp && *pp == '\\')
49 pp++;
50 if (*pp == '"')
51 len++;
52 }
53 len++;
54 p++;
55 }
56
57 q = new_arg = g_malloc (len + need_dblquotes*2 + 1);
58 p = argv;
59
60 if (need_dblquotes)
61 *q++ = '"';
62
63 while (*p) {
64 if (*p == '"')
65 *q++ = '\\';
66 else if (*p == '\\') {
67 const gchar *pp = p;
68
69 while (*pp && *pp == '\\')
70 pp++;
71 if (*pp == '"')
72 *q++ = '\\';
73 }
74 *q++ = *p;
75 p++;
76 }
77
78 if (need_dblquotes)
79 *q++ = '"';
80 *q++ = '\0';
81
82 return new_arg;
83 }
84
85 /*
86 * Generate a UTF-8 string for a Windows error.
87 */
88
89 /*
90 * We make the buffer at least this big, under the assumption that doing
91 * so will reduce the number of reallocations to do. (Otherwise, why
92 * did Microsoft bother supporting a minimum buffer size?)
93 */
94 #define ERRBUF_SIZE 128
95 const char *
win32strerror(DWORD error)96 win32strerror(DWORD error)
97 {
98 DWORD retval;
99 WCHAR *utf16_message;
100 char *utf8_message;
101 char *tempmsg;
102 const char *msg;
103
104 /*
105 * XXX - what language ID to use?
106 *
107 * For UN*Xes, g_strerror() may or may not return localized strings.
108 *
109 * We currently don't have localized strings, except for GUI items,
110 * but we might want to do so. On the other hand, if most of these
111 * messages are going to be read by Wireshark developers, English
112 * might be a better choice, so the developer doesn't have to get
113 * the message translated if it's in a language they don't happen
114 * to understand. Then again, we're including the error number,
115 * so the developer can just look that up.
116 */
117 retval = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK,
118 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
119 (LPTSTR)&utf16_message, ERRBUF_SIZE, NULL);
120 if (retval == 0) {
121 /* Failed. */
122 tempmsg = g_strdup_printf("Couldn't get error message for error (%lu) (because %lu)",
123 error, GetLastError());
124 msg = g_intern_string(tempmsg);
125 g_free(tempmsg);
126 return msg;
127 }
128
129 utf8_message = g_utf16_to_utf8(utf16_message, -1, NULL, NULL, NULL);
130 LocalFree(utf16_message);
131 if (utf8_message == NULL) {
132 /* Conversion failed. */
133 tempmsg = g_strdup_printf("Couldn't convert error message for error to UTF-8 (%lu) (because %lu)",
134 error, GetLastError());
135 msg = g_intern_string(tempmsg);
136 g_free(tempmsg);
137 return msg;
138 }
139 tempmsg = g_strdup_printf("%s (%lu)", utf8_message, error);
140 g_free(utf8_message);
141 msg = g_intern_string(tempmsg);
142 g_free(tempmsg);
143 return msg;
144 }
145
146 /*
147 * Generate a string for a Win32 exception code.
148 */
149 const char *
win32strexception(DWORD exception)150 win32strexception(DWORD exception)
151 {
152 static char errbuf[ERRBUF_SIZE+1];
153 static const struct exception_msg {
154 int code;
155 char *msg;
156 } exceptions[] = {
157 { EXCEPTION_ACCESS_VIOLATION, "Access violation" },
158 { EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "Array bounds exceeded" },
159 { EXCEPTION_BREAKPOINT, "Breakpoint" },
160 { EXCEPTION_DATATYPE_MISALIGNMENT, "Data type misalignment" },
161 { EXCEPTION_FLT_DENORMAL_OPERAND, "Denormal floating-point operand" },
162 { EXCEPTION_FLT_DIVIDE_BY_ZERO, "Floating-point divide by zero" },
163 { EXCEPTION_FLT_INEXACT_RESULT, "Floating-point inexact result" },
164 { EXCEPTION_FLT_INVALID_OPERATION, "Invalid floating-point operation" },
165 { EXCEPTION_FLT_OVERFLOW, "Floating-point overflow" },
166 { EXCEPTION_FLT_STACK_CHECK, "Floating-point stack check" },
167 { EXCEPTION_FLT_UNDERFLOW, "Floating-point underflow" },
168 { EXCEPTION_GUARD_PAGE, "Guard page violation" },
169 { EXCEPTION_ILLEGAL_INSTRUCTION, "Illegal instruction" },
170 { EXCEPTION_IN_PAGE_ERROR, "Page-in error" },
171 { EXCEPTION_INT_DIVIDE_BY_ZERO, "Integer divide by zero" },
172 { EXCEPTION_INT_OVERFLOW, "Integer overflow" },
173 { EXCEPTION_INVALID_DISPOSITION, "Invalid disposition" },
174 { EXCEPTION_INVALID_HANDLE, "Invalid handle" },
175 { EXCEPTION_NONCONTINUABLE_EXCEPTION, "Non-continuable exception" },
176 { EXCEPTION_PRIV_INSTRUCTION, "Privileged instruction" },
177 { EXCEPTION_SINGLE_STEP, "Single-step complete" },
178 { EXCEPTION_STACK_OVERFLOW, "Stack overflow" },
179 { 0, NULL }
180 };
181 #define N_EXCEPTIONS (sizeof exceptions / sizeof exceptions[0])
182 int i;
183
184 for (i = 0; i < N_EXCEPTIONS; i++) {
185 if (exceptions[i].code == exception)
186 return exceptions[i].msg;
187 }
188 g_snprintf(errbuf, (gulong)sizeof errbuf, "Exception 0x%08x", exception);
189 return errbuf;
190 }
191
192 // This appears to be the closest equivalent to SIGPIPE on Windows.
193 // https://devblogs.microsoft.com/oldnewthing/?p=2433
194 // https://stackoverflow.com/a/53214/82195
195
win32_kill_child_on_exit(HANDLE child_handle)196 static void win32_kill_child_on_exit(HANDLE child_handle) {
197 static HANDLE cjo_handle = NULL;
198 if (!cjo_handle) {
199 cjo_handle = CreateJobObject(NULL, NULL);
200
201 if (!cjo_handle) {
202 ws_log(LOG_DOMAIN_CAPTURE, LOG_LEVEL_DEBUG, "Could not create child cleanup job object: %s",
203 win32strerror(GetLastError()));
204 return;
205 }
206
207 JOBOBJECT_EXTENDED_LIMIT_INFORMATION cjo_jel_info = { 0 };
208 cjo_jel_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
209 BOOL sijo_ret = SetInformationJobObject(cjo_handle, JobObjectExtendedLimitInformation,
210 &cjo_jel_info, sizeof(cjo_jel_info));
211 if (!sijo_ret) {
212 ws_log(LOG_DOMAIN_CAPTURE, LOG_LEVEL_DEBUG, "Could not set child cleanup limits: %s",
213 win32strerror(GetLastError()));
214 }
215 }
216
217 BOOL aptjo_ret = AssignProcessToJobObject(cjo_handle, child_handle);
218 if (!aptjo_ret) {
219 ws_log(LOG_DOMAIN_CAPTURE, LOG_LEVEL_DEBUG, "Could not assign child cleanup process: %s",
220 win32strerror(GetLastError()));
221 }
222 }
223
win32_create_process(const char * application_name,const char * command_line,LPSECURITY_ATTRIBUTES process_attributes,LPSECURITY_ATTRIBUTES thread_attributes,BOOL inherit_handles,DWORD creation_flags,LPVOID environment,const char * current_directory,LPSTARTUPINFO startup_info,LPPROCESS_INFORMATION process_information)224 BOOL win32_create_process(const char *application_name, const char *command_line, LPSECURITY_ATTRIBUTES process_attributes, LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD creation_flags, LPVOID environment, const char *current_directory, LPSTARTUPINFO startup_info, LPPROCESS_INFORMATION process_information)
225 {
226 gunichar2 *wappname = NULL, *wcurrentdirectory = NULL;
227 gunichar2 *wcommandline = g_utf8_to_utf16(command_line, -1, NULL, NULL, NULL);
228 // CREATE_SUSPENDED: Suspend the child so that we can cleanly call
229 // AssignProcessToJobObject.
230 DWORD wcreationflags = creation_flags|CREATE_SUSPENDED;
231 // CREATE_BREAKAWAY_FROM_JOB: The main application might be associated with a job,
232 // e.g. if we're running under "Run As", ConEmu, or Visual Studio. On Windows
233 // <= 7 our child process needs to break away from it so that we can cleanly
234 // call AssignProcessToJobObject on *our* job.
235 // Windows >= 8 supports nested jobs so this isn't necessary there.
236 // https://blogs.msdn.microsoft.com/winsdk/2014/09/22/job-object-insanity/
237 //
238 if (! IsWindowsVersionOrGreater(6, 2, 0)) { // Windows 8
239 wcreationflags |= CREATE_BREAKAWAY_FROM_JOB;
240 }
241
242 if (application_name) {
243 wappname = g_utf8_to_utf16(application_name, -1, NULL, NULL, NULL);
244 }
245 if (current_directory) {
246 wcurrentdirectory = g_utf8_to_utf16(current_directory, -1, NULL, NULL, NULL);
247 }
248 BOOL cp_res = CreateProcess(wappname, wcommandline, process_attributes, thread_attributes,
249 inherit_handles, wcreationflags, environment, wcurrentdirectory, startup_info,
250 process_information);
251 if (cp_res) {
252 win32_kill_child_on_exit(process_information->hProcess);
253 ResumeThread(process_information->hThread);
254 }
255 // XXX Else try again if CREATE_BREAKAWAY_FROM_JOB and GetLastError() == ERROR_ACCESS_DENIED?
256
257 g_free(wappname);
258 g_free(wcommandline);
259 g_free(wcurrentdirectory);
260 return cp_res;
261 }
262
263 /*
264 * Editor modelines - https://www.wireshark.org/tools/modelines.html
265 *
266 * Local Variables:
267 * c-basic-offset: 4
268 * tab-width: 8
269 * indent-tabs-mode: nil
270 * End:
271 *
272 * ex: set shiftwidth=4 tabstop=8 expandtab:
273 * :indentSize=4:tabSize=8:noTabs=true:
274 */
275