summaryrefslogtreecommitdiffstats
path: root/plugins/netutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/netutils.c')
-rw-r--r--plugins/netutils.c447
1 files changed, 229 insertions, 218 deletions
diff --git a/plugins/netutils.c b/plugins/netutils.c
index c6af248e..b4c6ff0a 100644
--- a/plugins/netutils.c
+++ b/plugins/netutils.c
@@ -1,40 +1,43 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring Plugins network utilities 3 * Monitoring Plugins network utilities
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 1999 Ethan Galstad (nagios@nagios.org) 6 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
7* Copyright (c) 2003-2008 Monitoring Plugins Development Team 7 * Copyright (c) 2003-2024 Monitoring Plugins Development Team
8* 8 *
9* Description: 9 * Description:
10* 10 *
11* This file contains commons functions used in many of the plugins. 11 * This file contains commons functions used in many of the plugins.
12* 12 *
13* 13 *
14* This program is free software: you can redistribute it and/or modify 14 * This program is free software: you can redistribute it and/or modify
15* it under the terms of the GNU General Public License as published by 15 * it under the terms of the GNU General Public License as published by
16* the Free Software Foundation, either version 3 of the License, or 16 * the Free Software Foundation, either version 3 of the License, or
17* (at your option) any later version. 17 * (at your option) any later version.
18* 18 *
19* This program is distributed in the hope that it will be useful, 19 * This program is distributed in the hope that it will be useful,
20* but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22* GNU General Public License for more details. 22 * GNU General Public License for more details.
23* 23 *
24* You should have received a copy of the GNU General Public License 24 * You should have received a copy of the GNU General Public License
25* along with this program. If not, see <http://www.gnu.org/licenses/>. 25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26* 26 *
27* 27 *
28*****************************************************************************/ 28 *****************************************************************************/
29 29
30#include "common.h" 30#include "common.h"
31#include "output.h"
32#include "states.h"
33#include <sys/types.h>
31#include "netutils.h" 34#include "netutils.h"
32 35
33unsigned int socket_timeout = DEFAULT_SOCKET_TIMEOUT; 36unsigned int socket_timeout = DEFAULT_SOCKET_TIMEOUT;
34unsigned int socket_timeout_state = STATE_CRITICAL; 37mp_state_enum socket_timeout_state = STATE_CRITICAL;
35 38mp_state_enum econn_refuse_state = STATE_CRITICAL;
36int econn_refuse_state = STATE_CRITICAL;
37bool was_refused = false; 39bool was_refused = false;
40
38#if USE_IPV6 41#if USE_IPV6
39int address_family = AF_UNSPEC; 42int address_family = AF_UNSPEC;
40#else 43#else
@@ -42,177 +45,177 @@ int address_family = AF_INET;
42#endif 45#endif
43 46
44/* handles socket timeouts */ 47/* handles socket timeouts */
45void 48void socket_timeout_alarm_handler(int sig) {
46socket_timeout_alarm_handler (int sig) 49 mp_subcheck timeout_sc = mp_subcheck_init();
47{ 50 timeout_sc = mp_set_subcheck_state(timeout_sc, socket_timeout_state);
48 if (sig == SIGALRM) 51
49 printf (_("%s - Socket timeout after %d seconds\n"), state_text(socket_timeout_state), socket_timeout); 52 if (sig == SIGALRM) {
50 else 53 xasprintf(&timeout_sc.output, _("Socket timeout after %d seconds\n"), socket_timeout);
51 printf (_("%s - Abnormal timeout after %d seconds\n"), state_text(socket_timeout_state), socket_timeout); 54 } else {
52 55 xasprintf(&timeout_sc.output, _("Abnormal timeout after %d seconds\n"), socket_timeout);
53 exit (socket_timeout_state); 56 }
54} 57
58 mp_check overall = mp_check_init();
59 mp_add_subcheck_to_check(&overall, timeout_sc);
55 60
61 mp_exit(overall);
62}
56 63
57/* connects to a host on a specified tcp port, sends a string, and gets a 64/* connects to a host on a specified tcp port, sends a string, and gets a
58 response. loops on select-recv until timeout or eof to get all of a 65 response. loops on select-recv until timeout or eof to get all of a
59 multi-packet answer */ 66 multi-packet answer */
60int 67mp_state_enum process_tcp_request2(const char *server_address, const int server_port,
61process_tcp_request2 (const char *server_address, int server_port, 68 const char *send_buffer, char *recv_buffer,
62 const char *send_buffer, char *recv_buffer, int recv_size) 69 const int recv_size) {
63{
64 70
65 int result; 71 int socket;
66 int send_result;
67 int recv_result;
68 int sd;
69 struct timeval tv;
70 fd_set readfds;
71 int recv_length = 0;
72 72
73 result = np_net_connect (server_address, server_port, &sd, IPPROTO_TCP); 73 mp_state_enum connect_result =
74 if (result != STATE_OK) 74 np_net_connect(server_address, server_port, &socket, IPPROTO_TCP);
75 if (connect_result != STATE_OK) {
75 return STATE_CRITICAL; 76 return STATE_CRITICAL;
77 }
76 78
77 send_result = send (sd, send_buffer, strlen (send_buffer), 0); 79 mp_state_enum result;
78 if (send_result<0 || (size_t)send_result!=strlen(send_buffer)) { 80 ssize_t send_result = send(socket, send_buffer, strlen(send_buffer), 0);
79 printf ("%s\n", _("Send failed")); 81 if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) {
82 // printf("%s\n", _("Send failed"));
80 result = STATE_WARNING; 83 result = STATE_WARNING;
81 } 84 }
82 85
83 while (1) { 86 fd_set readfds;
87 ssize_t recv_length = 0;
88 while (true) {
84 /* wait up to the number of seconds for socket timeout 89 /* wait up to the number of seconds for socket timeout
85 minus one for data from the host */ 90 minus one for data from the host */
86 tv.tv_sec = socket_timeout - 1; 91 struct timeval timeout = {
87 tv.tv_usec = 0; 92 .tv_sec = socket_timeout - 1,
88 FD_ZERO (&readfds); 93 .tv_usec = 0,
89 FD_SET (sd, &readfds); 94 };
90 select (sd + 1, &readfds, NULL, NULL, &tv); 95 FD_ZERO(&readfds);
96 FD_SET(socket, &readfds);
97 select(socket + 1, &readfds, NULL, NULL, &timeout);
91 98
92 /* make sure some data has arrived */ 99 /* make sure some data has arrived */
93 if (!FD_ISSET (sd, &readfds)) { /* it hasn't */ 100 if (!FD_ISSET(socket, &readfds)) { /* it hasn't */
94 if (!recv_length) { 101 if (!recv_length) {
95 strcpy (recv_buffer, ""); 102 strcpy(recv_buffer, "");
96 printf ("%s\n", _("No data was received from host!")); 103 // printf("%s\n", _("No data was received from host!"));
97 result = STATE_WARNING; 104 result = STATE_WARNING;
98 } 105 } else { /* this one failed, but previous ones worked */
99 else { /* this one failed, but previous ones worked */
100 recv_buffer[recv_length] = 0; 106 recv_buffer[recv_length] = 0;
101 } 107 }
102 break; 108 break;
109 } /* it has */
110
111 ssize_t recv_result =
112 recv(socket, recv_buffer + recv_length, (size_t)(recv_size - recv_length - 1), 0);
113 if (recv_result == -1) {
114 /* recv failed, bail out */
115 strcpy(recv_buffer + recv_length, "");
116 result = STATE_WARNING;
117 break;
103 } 118 }
104 else { /* it has */ 119
105 recv_result = 120 if (recv_result == 0) {
106 recv (sd, recv_buffer + recv_length, 121 /* end of file ? */
107 (size_t)recv_size - recv_length - 1, 0); 122 recv_buffer[recv_length] = 0;
108 if (recv_result == -1) { 123 break;
109 /* recv failed, bail out */ 124 }
110 strcpy (recv_buffer + recv_length, ""); 125
111 result = STATE_WARNING; 126 /* we got data! */
112 break; 127 recv_length += recv_result;
113 } 128 if (recv_length >= recv_size - 1) {
114 else if (recv_result == 0) { 129 /* buffer full, we're done */
115 /* end of file ? */ 130 recv_buffer[recv_size - 1] = 0;
116 recv_buffer[recv_length] = 0; 131 break;
117 break;
118 }
119 else { /* we got data! */
120 recv_length += recv_result;
121 if (recv_length >= recv_size - 1) {
122 /* buffer full, we're done */
123 recv_buffer[recv_size - 1] = 0;
124 break;
125 }
126 }
127 } 132 }
128 /* end if(!FD_ISSET(sd,&readfds)) */ 133 /* end if(!FD_ISSET(sd,&readfds)) */
129 } 134 }
130 /* end while(1) */
131 135
132 close (sd); 136 close(socket);
133 return result; 137 return result;
134} 138}
135 139
136
137/* connects to a host on a specified port, sends a string, and gets a 140/* connects to a host on a specified port, sends a string, and gets a
138 response */ 141 response */
139int 142mp_state_enum process_request(const char *server_address, const int server_port, const int proto,
140process_request (const char *server_address, int server_port, int proto, 143 const char *send_buffer, char *recv_buffer, const int recv_size) {
141 const char *send_buffer, char *recv_buffer, int recv_size)
142{
143 int result;
144 int sd;
145
146 result = STATE_OK;
147 144
148 result = np_net_connect (server_address, server_port, &sd, proto); 145 mp_state_enum result = STATE_OK;
149 if (result != STATE_OK) 146 int socket;
147 result = np_net_connect(server_address, server_port, &socket, proto);
148 if (result != STATE_OK) {
150 return STATE_CRITICAL; 149 return STATE_CRITICAL;
150 }
151 151
152 result = send_request (sd, proto, send_buffer, recv_buffer, recv_size); 152 result = send_request(socket, proto, send_buffer, recv_buffer, recv_size);
153 153
154 close (sd); 154 close(socket);
155 155
156 return result; 156 return result;
157} 157}
158 158
159
160/* opens a tcp or udp connection to a remote host or local socket */ 159/* opens a tcp or udp connection to a remote host or local socket */
161int 160mp_state_enum np_net_connect(const char *host_name, int port, int *socketDescriptor,
162np_net_connect (const char *host_name, int port, int *sd, int proto) 161 const int proto) {
163{ 162 /* send back STATE_UNKOWN if there's an error
164 /* send back STATE_UNKOWN if there's an error 163 send back STATE_OK if we connect
165 send back STATE_OK if we connect 164 send back STATE_CRITICAL if we can't connect.
166 send back STATE_CRITICAL if we can't connect. 165 Let upstream figure out what to send to the user. */
167 Let upstream figure out what to send to the user. */ 166 bool is_socket = (host_name[0] == '/');
168 struct addrinfo hints; 167 int socktype = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
169 struct addrinfo *r, *res; 168
170 struct sockaddr_un su; 169 struct addrinfo hints = {};
171 char port_str[6], host[MAX_HOST_ADDRESS_LENGTH]; 170 struct addrinfo *res = NULL;
172 size_t len; 171 int result;
173 int socktype, result;
174 short is_socket = (host_name[0] == '/');
175
176 socktype = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
177
178 /* as long as it doesn't start with a '/', it's assumed a host or ip */ 172 /* as long as it doesn't start with a '/', it's assumed a host or ip */
179 if (!is_socket){ 173 if (!is_socket) {
180 memset (&hints, 0, sizeof (hints)); 174 memset(&hints, 0, sizeof(hints));
181 hints.ai_family = address_family; 175 hints.ai_family = address_family;
182 hints.ai_protocol = proto; 176 hints.ai_protocol = proto;
183 hints.ai_socktype = socktype; 177 hints.ai_socktype = socktype;
184 178
185 len = strlen (host_name); 179 size_t len = strlen(host_name);
186 /* check for an [IPv6] address (and strip the brackets) */ 180 /* check for an [IPv6] address (and strip the brackets) */
187 if (len >= 2 && host_name[0] == '[' && host_name[len - 1] == ']') { 181 if (len >= 2 && host_name[0] == '[' && host_name[len - 1] == ']') {
188 host_name++; 182 host_name++;
189 len -= 2; 183 len -= 2;
190 } 184 }
191 if (len >= sizeof(host)) 185
186 char host[MAX_HOST_ADDRESS_LENGTH];
187
188 if (len >= sizeof(host)) {
192 return STATE_UNKNOWN; 189 return STATE_UNKNOWN;
193 memcpy (host, host_name, len); 190 }
191
192 memcpy(host, host_name, len);
194 host[len] = '\0'; 193 host[len] = '\0';
195 snprintf (port_str, sizeof (port_str), "%d", port);
196 result = getaddrinfo (host, port_str, &hints, &res);
197 194
198 if (result != 0) { 195 char port_str[6];
199 printf ("%s\n", gai_strerror (result)); 196 snprintf(port_str, sizeof(port_str), "%d", port);
197 int getaddrinfo_err = getaddrinfo(host, port_str, &hints, &res);
198
199 if (getaddrinfo_err != 0) {
200 // printf("%s\n", gai_strerror(result));
200 return STATE_UNKNOWN; 201 return STATE_UNKNOWN;
201 } 202 }
202 203
203 r = res; 204 struct addrinfo *addressPointer = res;
204 while (r) { 205 while (addressPointer) {
205 /* attempt to create a socket */ 206 /* attempt to create a socket */
206 *sd = socket (r->ai_family, socktype, r->ai_protocol); 207 *socketDescriptor =
208 socket(addressPointer->ai_family, socktype, addressPointer->ai_protocol);
207 209
208 if (*sd < 0) { 210 if (*socketDescriptor < 0) {
209 printf ("%s\n", _("Socket creation failed")); 211 // printf("%s\n", _("Socket creation failed"));
210 freeaddrinfo (r); 212 freeaddrinfo(addressPointer);
211 return STATE_UNKNOWN; 213 return STATE_UNKNOWN;
212 } 214 }
213 215
214 /* attempt to open a connection */ 216 /* attempt to open a connection */
215 result = connect (*sd, r->ai_addr, r->ai_addrlen); 217 result =
218 connect(*socketDescriptor, addressPointer->ai_addr, addressPointer->ai_addrlen);
216 219
217 if (result == 0) { 220 if (result == 0) {
218 was_refused = false; 221 was_refused = false;
@@ -227,149 +230,157 @@ np_net_connect (const char *host_name, int port, int *sd, int proto)
227 } 230 }
228 } 231 }
229 232
230 close (*sd); 233 close(*socketDescriptor);
231 r = r->ai_next; 234 addressPointer = addressPointer->ai_next;
232 } 235 }
233 freeaddrinfo (res); 236
234 } 237 freeaddrinfo(res);
235 /* else the hostname is interpreted as a path to a unix socket */ 238
236 else { 239 } else {
237 if(strlen(host_name) >= UNIX_PATH_MAX){ 240 /* else the hostname is interpreted as a path to a unix socket */
241 if (strlen(host_name) >= UNIX_PATH_MAX) {
238 die(STATE_UNKNOWN, _("Supplied path too long unix domain socket")); 242 die(STATE_UNKNOWN, _("Supplied path too long unix domain socket"));
239 } 243 }
240 memset(&su, 0, sizeof(su)); 244
245 struct sockaddr_un su = {};
241 su.sun_family = AF_UNIX; 246 su.sun_family = AF_UNIX;
242 strncpy(su.sun_path, host_name, UNIX_PATH_MAX); 247 strncpy(su.sun_path, host_name, UNIX_PATH_MAX);
243 *sd = socket(PF_UNIX, SOCK_STREAM, 0); 248 *socketDescriptor = socket(PF_UNIX, SOCK_STREAM, 0);
244 if(*sd < 0){ 249
250 if (*socketDescriptor < 0) {
245 die(STATE_UNKNOWN, _("Socket creation failed")); 251 die(STATE_UNKNOWN, _("Socket creation failed"));
246 } 252 }
247 result = connect(*sd, (struct sockaddr *)&su, sizeof(su)); 253
248 if (result < 0 && errno == ECONNREFUSED) 254 result = connect(*socketDescriptor, (struct sockaddr *)&su, sizeof(su));
255 if (result < 0 && errno == ECONNREFUSED) {
249 was_refused = true; 256 was_refused = true;
257 }
250 } 258 }
251 259
252 if (result == 0) 260 if (result == 0) {
253 return STATE_OK; 261 return STATE_OK;
254 else if (was_refused) { 262 }
263
264 if (was_refused) {
255 switch (econn_refuse_state) { /* a user-defined expected outcome */ 265 switch (econn_refuse_state) { /* a user-defined expected outcome */
256 case STATE_OK: 266 case STATE_OK:
257 case STATE_WARNING: /* user wants WARN or OK on refusal, or... */ 267 case STATE_WARNING: /* user wants WARN or OK on refusal, or... */
258 case STATE_CRITICAL: /* user did not set econn_refuse_state, or wanted critical */ 268 case STATE_CRITICAL: /* user did not set econn_refuse_state, or wanted critical */
259 if (is_socket) 269 if (is_socket) {
260 printf("connect to file socket %s: %s\n", host_name, strerror(errno)); 270 // printf("connect to file socket %s: %s\n", host_name, strerror(errno));
261 else 271 } else {
262 printf("connect to address %s and port %d: %s\n", 272 // printf("connect to address %s and port %d: %s\n", host_name, port,
263 host_name, port, strerror(errno)); 273 // strerror(errno));
274 }
264 return STATE_CRITICAL; 275 return STATE_CRITICAL;
265 break; 276 break;
266 default: /* it's a logic error if we do not end up in STATE_(OK|WARNING|CRITICAL) */ 277 default: /* it's a logic error if we do not end up in STATE_(OK|WARNING|CRITICAL) */
267 return STATE_UNKNOWN; 278 return STATE_UNKNOWN;
268 break; 279 break;
269 } 280 }
270 } 281 } else {
271 else { 282 if (is_socket) {
272 if (is_socket) 283 // printf("connect to file socket %s: %s\n", host_name, strerror(errno));
273 printf("connect to file socket %s: %s\n", host_name, strerror(errno)); 284 } else {
274 else 285 // printf("connect to address %s and port %d: %s\n", host_name, port, strerror(errno));
275 printf("connect to address %s and port %d: %s\n", 286 }
276 host_name, port, strerror(errno));
277 return STATE_CRITICAL; 287 return STATE_CRITICAL;
278 } 288 }
279} 289}
280 290
281int 291mp_state_enum send_request(const int socket, const int proto, const char *send_buffer,
282send_request (int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size) 292 char *recv_buffer, const int recv_size) {
283{ 293 mp_state_enum result = STATE_OK;
284 int result = STATE_OK;
285 int send_result;
286 int recv_result;
287 struct timeval tv;
288 fd_set readfds;
289 294
290 send_result = send (sd, send_buffer, strlen (send_buffer), 0); 295 ssize_t send_result = send(socket, send_buffer, strlen(send_buffer), 0);
291 if (send_result<0 || (size_t)send_result!=strlen(send_buffer)) { 296 if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) {
292 printf ("%s\n", _("Send failed")); 297 // printf("%s\n", _("Send failed"));
293 result = STATE_WARNING; 298 result = STATE_WARNING;
294 } 299 }
295 300
296 /* wait up to the number of seconds for socket timeout minus one 301 /* wait up to the number of seconds for socket timeout minus one
297 for data from the host */ 302 for data from the host */
298 tv.tv_sec = socket_timeout - 1; 303 struct timeval timestamp = {
299 tv.tv_usec = 0; 304 .tv_sec = socket_timeout - 1,
300 FD_ZERO (&readfds); 305 .tv_usec = 0,
301 FD_SET (sd, &readfds); 306 };
302 select (sd + 1, &readfds, NULL, NULL, &tv); 307 fd_set readfds;
308 FD_ZERO(&readfds);
309 FD_SET(socket, &readfds);
310 select(socket + 1, &readfds, NULL, NULL, &timestamp);
303 311
304 /* make sure some data has arrived */ 312 /* make sure some data has arrived */
305 if (!FD_ISSET (sd, &readfds)) { 313 if (!FD_ISSET(socket, &readfds)) {
306 strcpy (recv_buffer, ""); 314 strcpy(recv_buffer, "");
307 printf ("%s\n", _("No data was received from host!")); 315 // printf("%s\n", _("No data was received from host!"));
308 result = STATE_WARNING; 316 result = STATE_WARNING;
309 } 317 } else {
310 318 ssize_t recv_result = recv(socket, recv_buffer, (size_t)(recv_size - 1), 0);
311 else {
312 recv_result = recv (sd, recv_buffer, (size_t)recv_size - 1, 0);
313 if (recv_result == -1) { 319 if (recv_result == -1) {
314 strcpy (recv_buffer, ""); 320 strcpy(recv_buffer, "");
315 if (proto != IPPROTO_TCP) 321 if (proto != IPPROTO_TCP) {
316 printf ("%s\n", _("Receive failed")); 322 // printf("%s\n", _("Receive failed"));
323 }
317 result = STATE_WARNING; 324 result = STATE_WARNING;
318 } 325 } else {
319 else
320 recv_buffer[recv_result] = 0; 326 recv_buffer[recv_result] = 0;
327 }
321 328
322 /* die returned string */ 329 /* die returned string */
323 recv_buffer[recv_size - 1] = 0; 330 recv_buffer[recv_size - 1] = 0;
324 } 331 }
332
325 return result; 333 return result;
326} 334}
327 335
328 336bool is_host(const char *address) {
329bool is_host (const char *address) { 337 if (is_addr(address) || is_hostname(address)) {
330 if (is_addr (address) || is_hostname (address))
331 return (true); 338 return (true);
339 }
332 340
333 return (false); 341 return (false);
334} 342}
335 343
336void 344void host_or_die(const char *str) {
337host_or_die(const char *str) 345 if (!str || (!is_addr(str) && !is_hostname(str))) {
338{
339 if(!str || (!is_addr(str) && !is_hostname(str)))
340 usage_va(_("Invalid hostname/address - %s"), str); 346 usage_va(_("Invalid hostname/address - %s"), str);
347 }
341} 348}
342 349
343bool is_addr (const char *address) { 350bool is_addr(const char *address) {
344#ifdef USE_IPV6 351#ifdef USE_IPV6
345 if (address_family == AF_INET && is_inet_addr (address)) 352 if (address_family == AF_INET && is_inet_addr(address)) {
346 return true; 353 return true;
347 else if (address_family == AF_INET6 && is_inet6_addr (address)) 354 }
355
356 if (address_family == AF_INET6 && is_inet6_addr(address)) {
348 return true; 357 return true;
358 }
349#else 359#else
350 if (is_inet_addr (address)) 360 if (is_inet_addr(address)) {
351 return (true); 361 return true;
362 }
352#endif 363#endif
353 364
354 return (false); 365 return false;
355} 366}
356 367
357int 368bool dns_lookup(const char *node_string, struct sockaddr_storage *ss, const int family) {
358dns_lookup (const char *in, struct sockaddr_storage *ss, int family)
359{
360 struct addrinfo hints; 369 struct addrinfo hints;
361 struct addrinfo *res; 370 memset(&hints, 0, sizeof(struct addrinfo));
362 int retval;
363
364 memset (&hints, 0, sizeof(struct addrinfo));
365 hints.ai_family = family; 371 hints.ai_family = family;
366 372
367 retval = getaddrinfo (in, NULL, &hints, &res); 373 struct addrinfo *res;
368 if (retval != 0) 374 int retval = getaddrinfo(node_string, NULL, &hints, &res);
375 if (retval != 0) {
369 return false; 376 return false;
377 }
378
379 if (ss != NULL) {
380 memcpy(ss, res->ai_addr, res->ai_addrlen);
381 }
382
383 freeaddrinfo(res);
370 384
371 if (ss != NULL)
372 memcpy (ss, res->ai_addr, res->ai_addrlen);
373 freeaddrinfo (res);
374 return true; 385 return true;
375} 386}