]>
Commit | Line | Data |
---|---|---|
f7f3304a | 1 | #include "squid.h" |
7c16470c AJ |
2 | |
3 | /* UNIX SMBlib NetBIOS implementation | |
4 | ||
5 | Version 1.0 | |
6 | SMBlib Routines | |
7 | ||
8 | Copyright (C) Richard Sharpe 1996 | |
9 | ||
10 | */ | |
11 | ||
12 | /* | |
13 | This program is free software; you can redistribute it and/or modify | |
14 | it under the terms of the GNU General Public License as published by | |
15 | the Free Software Foundation; either version 2 of the License, or | |
16 | (at your option) any later version. | |
17 | ||
18 | This program is distributed in the hope that it will be useful, | |
19 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | GNU General Public License for more details. | |
22 | ||
23 | You should have received a copy of the GNU General Public License | |
24 | along with this program; if not, write to the Free Software | |
25 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
26 | */ | |
27 | ||
28 | int SMBlib_errno; | |
29 | int SMBlib_SMB_Error; | |
30 | #define SMBLIB_ERRNO | |
31 | ||
32 | #include "smblib/smblib.h" | |
33 | #include "smblib/smblib-priv.h" | |
34 | #include "rfcnb/rfcnb.h" | |
35 | ||
36 | #include <signal.h> | |
37 | #if HAVE_STRING_H | |
38 | #include <string.h> | |
39 | #endif | |
40 | ||
41 | SMB_State_Types SMBlib_State; | |
42 | ||
43 | const char *SMB_Prots[] = {"PC NETWORK PROGRAM 1.0", | |
3c96dd46 A |
44 | "MICROSOFT NETWORKS 1.03", |
45 | "MICROSOFT NETWORKS 3.0", | |
46 | "DOS LANMAN1.0", | |
47 | "LANMAN1.0", | |
48 | "DOS LM1.2X002", | |
49 | "LM1.2X002", | |
50 | "DOS LANMAN2.1", | |
51 | "LANMAN2.1", | |
52 | "Samba", | |
53 | "NT LM 0.12", | |
54 | "NT LANMAN 1.0", | |
55 | NULL | |
56 | }; | |
7c16470c | 57 | |
7c16470c AJ |
58 | /* Initialize the SMBlib package */ |
59 | ||
60 | int SMB_Init() | |
61 | ||
62 | { | |
63 | ||
64 | SMBlib_State = SMB_State_Started; | |
65 | ||
66 | signal(SIGPIPE, SIG_IGN); /* Ignore these ... */ | |
67 | ||
68 | /* If SMBLIB_Instrument is defines, turn on the instrumentation stuff */ | |
69 | #ifdef SMBLIB_INSTRUMENT | |
70 | ||
71 | SMBlib_Instrument_Init(); | |
72 | ||
73 | #endif | |
74 | ||
75 | return 0; | |
76 | ||
77 | } | |
78 | ||
7c16470c AJ |
79 | /* SMB_Create: Create a connection structure and return for later use */ |
80 | /* We have other helper routines to set variables */ | |
81 | ||
82 | SMB_Handle_Type SMB_Create_Con_Handle() | |
83 | ||
84 | { | |
85 | ||
86 | SMBlib_errno = SMBlibE_NotImpl; | |
87 | return(NULL); | |
88 | ||
89 | } | |
90 | ||
7c16470c AJ |
91 | /* SMB_Connect_Server: Connect to a server, but don't negotiate protocol */ |
92 | /* or anything else ... */ | |
93 | ||
94 | SMB_Handle_Type SMB_Connect_Server(SMB_Handle_Type Con_Handle, | |
95 | char *server, const char *NTdomain) | |
96 | ||
97 | { | |
98 | SMB_Handle_Type con; | |
99 | char called[80], calling[80], *address; | |
100 | int i; | |
101 | ||
102 | /* Get a connection structure if one does not exist */ | |
103 | ||
104 | con = Con_Handle; | |
105 | ||
106 | if (Con_Handle == NULL) { | |
107 | ||
108 | if ((con = (struct SMB_Connect_Def *)malloc(sizeof(struct SMB_Connect_Def))) == NULL) { | |
109 | ||
7c16470c AJ |
110 | SMBlib_errno = SMBlibE_NoSpace; |
111 | return NULL; | |
112 | } | |
113 | ||
114 | } | |
115 | ||
116 | /* Init some things ... */ | |
117 | ||
118 | strcpy(con -> service, ""); | |
119 | strcpy(con -> username, ""); | |
120 | strcpy(con -> password, ""); | |
121 | strcpy(con -> sock_options, ""); | |
122 | strcpy(con -> address, ""); | |
123 | strcpy(con -> desthost, server); | |
124 | strcpy(con -> PDomain, NTdomain); | |
125 | strcpy(con -> OSName, SMBLIB_DEFAULT_OSNAME); | |
126 | strcpy(con -> LMType, SMBLIB_DEFAULT_LMTYPE); | |
127 | con -> first_tree = con -> last_tree = NULL; | |
128 | ||
129 | SMB_Get_My_Name(con -> myname, sizeof(con -> myname)); | |
130 | ||
131 | con -> port = 0; /* No port selected */ | |
132 | ||
133 | /* Get some things we need for the SMB Header */ | |
134 | ||
135 | con -> pid = getpid(); | |
136 | con -> mid = con -> pid; /* This will do for now ... */ | |
137 | con -> uid = 0; /* Until we have done a logon, no uid ... */ | |
138 | con -> gid = getgid(); | |
139 | ||
140 | /* Now connect to the remote end, but first upper case the name of the | |
141 | service we are going to call, sine some servers want it in uppercase */ | |
142 | ||
143 | for (i=0; i < strlen(server); i++) | |
144 | called[i] = toupper(server[i]); | |
145 | ||
146 | called[strlen(server)] = 0; /* Make it a string */ | |
147 | ||
148 | for (i=0; i < strlen(con -> myname); i++) | |
149 | calling[i] = toupper(con -> myname[i]); | |
150 | ||
151 | calling[strlen(con -> myname)] = 0; /* Make it a string */ | |
152 | ||
153 | if (strcmp(con -> address, "") == 0) | |
154 | address = con -> desthost; | |
155 | else | |
156 | address = con -> address; | |
157 | ||
158 | con -> Trans_Connect = RFCNB_Call(called, | |
159 | calling, | |
160 | address, /* Protocol specific */ | |
161 | con -> port); | |
162 | ||
163 | /* Did we get one? */ | |
164 | ||
165 | if (con -> Trans_Connect == NULL) { | |
166 | ||
167 | if (Con_Handle == NULL) { | |
168 | Con_Handle = NULL; | |
169 | free(con); | |
170 | } | |
171 | SMBlib_errno = -SMBlibE_CallFailed; | |
172 | return NULL; | |
173 | ||
174 | } | |
175 | ||
176 | return(con); | |
177 | ||
178 | } | |
179 | ||
180 | /* SMB_Connect: Connect to the indicated server */ | |
181 | /* If Con_Handle == NULL then create a handle and connect, otherwise */ | |
182 | /* use the handle passed */ | |
183 | ||
184 | const char *SMB_Prots_Restrict[] = {"PC NETWORK PROGRAM 1.0", | |
185 | NULL | |
186 | }; | |
187 | ||
7c16470c AJ |
188 | SMB_Handle_Type SMB_Connect(SMB_Handle_Type Con_Handle, |
189 | SMB_Tree_Handle *tree, | |
190 | char *service, | |
191 | char *username, | |
192 | char *password) | |
193 | ||
194 | { | |
195 | SMB_Handle_Type con; | |
196 | char *host, *address; | |
197 | char temp[80], called[80], calling[80]; | |
198 | int i; | |
199 | ||
200 | /* Get a connection structure if one does not exist */ | |
201 | ||
202 | con = Con_Handle; | |
203 | ||
204 | if (Con_Handle == NULL) { | |
205 | ||
206 | if ((con = (struct SMB_Connect_Def *)malloc(sizeof(struct SMB_Connect_Def))) == NULL) { | |
207 | ||
208 | SMBlib_errno = SMBlibE_NoSpace; | |
209 | return NULL; | |
210 | } | |
211 | ||
212 | } | |
213 | ||
214 | /* Init some things ... */ | |
215 | ||
216 | strcpy(con -> service, service); | |
217 | strcpy(con -> username, username); | |
218 | strcpy(con -> password, password); | |
219 | strcpy(con -> sock_options, ""); | |
220 | strcpy(con -> address, ""); | |
221 | strcpy(con -> PDomain, SMBLIB_DEFAULT_DOMAIN); | |
222 | strcpy(con -> OSName, SMBLIB_DEFAULT_OSNAME); | |
223 | strcpy(con -> LMType, SMBLIB_DEFAULT_LMTYPE); | |
224 | con -> first_tree = con -> last_tree = NULL; | |
225 | ||
226 | SMB_Get_My_Name(con -> myname, sizeof(con -> myname)); | |
227 | ||
228 | con -> port = 0; /* No port selected */ | |
229 | ||
230 | /* Get some things we need for the SMB Header */ | |
231 | ||
232 | con -> pid = getpid(); | |
233 | con -> mid = con -> pid; /* This will do for now ... */ | |
234 | con -> uid = 0; /* Until we have done a logon, no uid */ | |
235 | con -> gid = getgid(); | |
236 | ||
237 | /* Now figure out the host portion of the service */ | |
238 | ||
239 | strcpy(temp, service); | |
240 | host = strtok(temp, "/\\"); /* Separate host name portion */ | |
241 | strcpy(con -> desthost, host); | |
242 | ||
243 | /* Now connect to the remote end, but first upper case the name of the | |
244 | service we are going to call, sine some servers want it in uppercase */ | |
245 | ||
246 | for (i=0; i < strlen(host); i++) | |
247 | called[i] = toupper(host[i]); | |
248 | ||
249 | called[strlen(host)] = 0; /* Make it a string */ | |
250 | ||
251 | for (i=0; i < strlen(con -> myname); i++) | |
252 | calling[i] = toupper(con -> myname[i]); | |
253 | ||
254 | calling[strlen(con -> myname)] = 0; /* Make it a string */ | |
255 | ||
256 | if (strcmp(con -> address, "") == 0) | |
257 | address = con -> desthost; | |
258 | else | |
259 | address = con -> address; | |
260 | ||
261 | con -> Trans_Connect = RFCNB_Call(called, | |
262 | calling, | |
263 | address, /* Protocol specific */ | |
264 | con -> port); | |
265 | ||
266 | /* Did we get one? */ | |
267 | ||
268 | if (con -> Trans_Connect == NULL) { | |
269 | ||
270 | if (Con_Handle == NULL) { | |
271 | free(con); | |
272 | Con_Handle = NULL; | |
273 | } | |
274 | SMBlib_errno = -SMBlibE_CallFailed; | |
275 | return NULL; | |
276 | ||
277 | } | |
278 | ||
279 | /* Now, negotiate the protocol */ | |
280 | ||
281 | if (SMB_Negotiate(con, SMB_Prots_Restrict) < 0) { | |
282 | ||
283 | /* Hmmm what should we do here ... We have a connection, but could not | |
284 | negotiate ... */ | |
285 | ||
286 | return NULL; | |
287 | ||
288 | } | |
289 | ||
290 | /* Now connect to the service ... */ | |
291 | ||
292 | if ((*tree = SMB_TreeConnect(con, NULL, service, password, "A:")) == NULL) { | |
293 | ||
294 | return NULL; | |
295 | ||
296 | } | |
297 | ||
298 | return(con); | |
299 | ||
300 | } | |
301 | ||
302 | /* Logon to the server. That is, do a session setup if we can. We do not do */ | |
303 | /* Unicode yet! */ | |
304 | ||
305 | int SMB_Logon_Server(SMB_Handle_Type Con_Handle, char *UserName, | |
306 | char *PassWord, const char *NtDomain, int PreCrypted) | |
307 | ||
308 | { | |
309 | struct RFCNB_Pkt *pkt; | |
310 | int param_len, pkt_len, pass_len; | |
311 | char *p, pword[128]; | |
312 | ||
313 | /* First we need a packet etc ... but we need to know what protocol has */ | |
314 | /* been negotiated to figure out if we can do it and what SMB format to */ | |
315 | /* use ... */ | |
316 | ||
317 | if (Con_Handle -> protocol < SMB_P_LanMan1) { | |
318 | ||
319 | SMBlib_errno = SMBlibE_ProtLow; | |
320 | return(SMBlibE_BAD); | |
321 | ||
322 | } | |
323 | ||
324 | if (PreCrypted) { | |
325 | pass_len = 24; | |
326 | memcpy(pword, PassWord, 24); | |
327 | } else { | |
328 | strcpy(pword, PassWord); | |
329 | #ifdef PAM_SMB_ENC_PASS | |
330 | if (Con_Handle->encrypt_passwords) { | |
331 | pass_len = 24; | |
332 | SMBencrypt((uchar *) PassWord, (uchar *) Con_Handle->Encrypt_Key, (uchar *) pword); | |
333 | } else | |
334 | #endif | |
335 | pass_len = strlen(pword); | |
336 | } | |
337 | ||
338 | /* Now build the correct structure */ | |
339 | ||
340 | if (Con_Handle -> protocol < SMB_P_NT1) { | |
341 | ||
342 | /* We don't handle encrypted passwords ... */ | |
343 | ||
344 | param_len = strlen(UserName) + 1 + pass_len + 1 + | |
345 | (NtDomain!=NULL ? strlen(NtDomain) : strlen(Con_Handle->PDomain)) + 1 + | |
346 | strlen(Con_Handle -> OSName) + 1; | |
347 | ||
348 | pkt_len = SMB_ssetpLM_len + param_len; | |
349 | ||
350 | pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len); | |
351 | ||
352 | if (pkt == NULL) { | |
353 | ||
354 | SMBlib_errno = SMBlibE_NoSpace; | |
355 | return(SMBlibE_BAD); /* Should handle the error */ | |
356 | ||
357 | } | |
358 | ||
359 | memset(SMB_Hdr(pkt), 0, SMB_ssetpLM_len); | |
360 | SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */ | |
361 | *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBsesssetupX; | |
362 | SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Con_Handle -> pid); | |
363 | SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, 0); | |
364 | SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Con_Handle -> mid); | |
365 | SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Con_Handle -> uid); | |
366 | *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 10; | |
367 | *(SMB_Hdr(pkt) + SMB_hdr_axc_offset) = 0xFF; /* No extra command */ | |
368 | SSVAL(SMB_Hdr(pkt), SMB_hdr_axo_offset, 0); | |
369 | ||
370 | SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_mbs_offset, SMBLIB_MAX_XMIT); | |
371 | SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_mmc_offset, 2); | |
372 | SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_vcn_offset, Con_Handle -> pid); | |
373 | SIVAL(SMB_Hdr(pkt), SMB_ssetpLM_snk_offset, 0); | |
374 | SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_pwl_offset, pass_len + 1); | |
375 | SIVAL(SMB_Hdr(pkt), SMB_ssetpLM_res_offset, 0); | |
376 | SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_bcc_offset, param_len); | |
377 | ||
378 | /* Now copy the param strings in with the right stuff */ | |
379 | ||
380 | p = (char *)(SMB_Hdr(pkt) + SMB_ssetpLM_buf_offset); | |
381 | ||
382 | /* Copy in password, then the rest. Password has a null at end */ | |
383 | ||
384 | memcpy(p, pword, pass_len); | |
385 | ||
386 | p = p + pass_len + 1; | |
387 | ||
388 | strcpy(p, UserName); | |
389 | p = p + strlen(UserName); | |
390 | *p = 0; | |
391 | ||
392 | p = p + 1; | |
393 | ||
394 | if (NtDomain != NULL) { | |
395 | strcpy(p, Con_Handle -> PDomain); | |
396 | p = p + strlen(Con_Handle -> PDomain); | |
397 | } else { | |
398 | strcpy(p, NtDomain); | |
399 | p = p + strlen(NtDomain); | |
400 | } | |
401 | *p = 0; | |
402 | p = p + 1; | |
403 | ||
404 | strcpy(p, Con_Handle -> OSName); | |
405 | p = p + strlen(Con_Handle -> OSName); | |
406 | *p = 0; | |
407 | ||
408 | } else { | |
409 | ||
410 | /* We don't admit to UNICODE support ... */ | |
411 | ||
412 | param_len = strlen(UserName) + 1 + pass_len + | |
413 | strlen(Con_Handle -> PDomain) + 1 + | |
414 | strlen(Con_Handle -> OSName) + 1 + | |
415 | strlen(Con_Handle -> LMType) + 1; | |
416 | ||
417 | pkt_len = SMB_ssetpNTLM_len + param_len; | |
418 | ||
419 | pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len); | |
420 | ||
421 | if (pkt == NULL) { | |
422 | ||
423 | SMBlib_errno = SMBlibE_NoSpace; | |
424 | return(-1); /* Should handle the error */ | |
425 | ||
426 | } | |
427 | ||
428 | memset(SMB_Hdr(pkt), 0, SMB_ssetpNTLM_len); | |
429 | SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */ | |
430 | *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBsesssetupX; | |
431 | SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Con_Handle -> pid); | |
432 | SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, 0); | |
433 | SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Con_Handle -> mid); | |
434 | SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Con_Handle -> uid); | |
435 | *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 13; | |
436 | *(SMB_Hdr(pkt) + SMB_hdr_axc_offset) = 0xFF; /* No extra command */ | |
437 | SSVAL(SMB_Hdr(pkt), SMB_hdr_axo_offset, 0); | |
438 | ||
439 | SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_mbs_offset, SMBLIB_MAX_XMIT); | |
440 | SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_mmc_offset, 0); | |
441 | SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_vcn_offset, 1); /* Thanks Tridge! */ | |
442 | SIVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_snk_offset, 0); | |
443 | SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_cipl_offset, pass_len); | |
444 | SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_cspl_offset, 0); | |
445 | SIVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_res_offset, 0); | |
446 | SIVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_cap_offset, 0); | |
447 | SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_bcc_offset, param_len); | |
448 | ||
449 | /* Now copy the param strings in with the right stuff */ | |
450 | ||
451 | p = (char *)(SMB_Hdr(pkt) + SMB_ssetpNTLM_buf_offset); | |
452 | ||
453 | /* Copy in password, then the rest. Password has no null at end */ | |
454 | ||
455 | memcpy(p, pword, pass_len); | |
456 | ||
457 | p = p + pass_len; | |
458 | ||
459 | strcpy(p, UserName); | |
460 | p = p + strlen(UserName); | |
461 | *p = 0; | |
462 | ||
463 | p = p + 1; | |
464 | ||
465 | strcpy(p, Con_Handle -> PDomain); | |
466 | p = p + strlen(Con_Handle -> PDomain); | |
467 | *p = 0; | |
468 | p = p + 1; | |
469 | ||
470 | strcpy(p, Con_Handle -> OSName); | |
471 | p = p + strlen(Con_Handle -> OSName); | |
472 | *p = 0; | |
473 | p = p + 1; | |
474 | ||
475 | strcpy(p, Con_Handle -> LMType); | |
476 | p = p + strlen(Con_Handle -> LMType); | |
477 | *p = 0; | |
478 | ||
479 | } | |
480 | ||
481 | /* Now send it and get a response */ | |
482 | ||
483 | if (RFCNB_Send(Con_Handle -> Trans_Connect, pkt, pkt_len) < 0) { | |
484 | ||
485 | #ifdef DEBUG | |
486 | fprintf(stderr, "Error sending SessSetupX request\n"); | |
487 | #endif | |
488 | ||
489 | RFCNB_Free_Pkt(pkt); | |
490 | SMBlib_errno = SMBlibE_SendFailed; | |
491 | return(SMBlibE_BAD); | |
492 | ||
493 | } | |
494 | ||
495 | /* Now get the response ... */ | |
496 | ||
497 | if (RFCNB_Recv(Con_Handle -> Trans_Connect, pkt, pkt_len) < 0) { | |
498 | ||
499 | #ifdef DEBUG | |
500 | fprintf(stderr, "Error receiving response to SessSetupAndX\n"); | |
501 | #endif | |
502 | ||
503 | RFCNB_Free_Pkt(pkt); | |
504 | SMBlib_errno = SMBlibE_RecvFailed; | |
505 | return(SMBlibE_BAD); | |
506 | ||
507 | } | |
508 | ||
509 | /* Check out the response type ... */ | |
510 | ||
511 | if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */ | |
512 | ||
513 | #ifdef DEBUG | |
514 | fprintf(stderr, "SMB_SessSetupAndX failed with errorclass = %i, Error Code = %i\n", | |
515 | CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset), | |
516 | SVAL(SMB_Hdr(pkt), SMB_hdr_err_offset)); | |
517 | #endif | |
518 | ||
519 | SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset); | |
520 | RFCNB_Free_Pkt(pkt); | |
521 | SMBlib_errno = SMBlibE_Remote; | |
522 | return(SMBlibE_BAD); | |
523 | ||
524 | } | |
525 | ||
526 | /** @@@ mdz: check for guest login { **/ | |
527 | if (SVAL(SMB_Hdr(pkt), SMB_ssetpr_act_offset) & 0x1) { | |
528 | /* do we allow guest login? NO! */ | |
529 | return (SMBlibE_BAD); | |
530 | } | |
531 | /** @@@ mdz: } **/ | |
532 | ||
533 | #ifdef DEBUG | |
534 | fprintf(stderr, "SessSetupAndX response. Action = %i\n", | |
535 | SVAL(SMB_Hdr(pkt), SMB_ssetpr_act_offset)); | |
536 | #endif | |
537 | ||
538 | /* Now pick up the UID for future reference ... */ | |
539 | ||
540 | Con_Handle -> uid = SVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset); | |
541 | RFCNB_Free_Pkt(pkt); | |
542 | ||
543 | return(0); | |
544 | ||
545 | } | |
546 | ||
7c16470c AJ |
547 | /* Disconnect from the server, and disconnect all tree connects */ |
548 | ||
549 | int SMB_Discon(SMB_Handle_Type Con_Handle, BOOL KeepHandle) | |
550 | ||
551 | { | |
552 | ||
553 | /* We just disconnect the connection for now ... */ | |
554 | ||
555 | RFCNB_Hangup(Con_Handle -> Trans_Connect); | |
556 | ||
557 | if (!KeepHandle) | |
558 | free(Con_Handle); | |
559 | ||
560 | return(0); | |
561 | ||
562 | } |