use new RCSID macro to prevent Id keyword from being optimized out
[freeradius.git] / src / modules / rlm_smb / rfcnb-util.c
1 /* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
2
3    Version 1.0
4    RFCNB Utility Routines ...
5
6    Copyright (C) Richard Sharpe 1996
7    Copyright 2006 The FreeRADIUS server project
8
9 */
10
11 /*
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 2 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program; if not, write to the Free Software
24    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 */
26
27 #include <freeradius-devel/ident.h>
28 RCSID("$Id$")
29
30 #include <string.h>
31
32 #include <freeradius-devel/autoconf.h>
33 #include <freeradius-devel/libradius.h>
34 #include "std-includes.h"
35 #include "rfcnb-priv.h"
36 #include "rfcnb-util.h"
37 #include "rfcnb-io.h"
38
39 extern void (*Prot_Print_Routine)(); /* Pointer to protocol print routine */
40
41 /* Convert name and pad to 16 chars as needed */
42 /* Name 1 is a C string with null termination, name 2 may not be */
43 /* If SysName is true, then put a <00> on end, else space>       */
44
45 void RFCNB_CvtPad_Name(char *name1, char *name2)
46
47 { char c, c1, c2;
48   int i, len;
49
50   len = strlen(name1);
51
52   for (i = 0; i < 16; i++) {
53
54     if (i >= len) {
55
56      c1 = 'C'; c2 = 'A'; /* CA is a space */
57
58     } else {
59
60       c = name1[i];
61       c1 = (char)((int)c/16 + (int)'A');
62       c2 = (char)((int)c%16 + (int)'A');
63     }
64
65     name2[i*2] = c1;
66     name2[i*2+1] = c2;
67
68   }
69
70   name2[32] = 0;   /* Put in the nll ...*/
71
72 }
73
74 /* Converts an Ascii NB Name (16 chars) to an RFCNB Name (32 chars)
75    Uses the encoding in RFC1001. Each nibble of byte is added to 'A'
76    to produce the next byte in the name.
77
78    This routine assumes that AName is 16 bytes long and that NBName has
79    space for 32 chars, so be careful ...
80
81 */
82
83 void RFCNB_AName_To_NBName(char *AName, char *NBName)
84
85 { char c, c1, c2;
86   int i;
87
88   for (i=0; i < 16; i++) {
89
90     c = AName[i];
91
92     c1 = (char)((c >> 4) + 'A');
93     c2 = (char)((c & 0xF) + 'A');
94
95     NBName[i*2] = c1;
96     NBName[i*2+1] = c2;
97   }
98
99   NBName[32] = 0; /* Put in a null */
100
101 }
102
103 /* Do the reverse of the above ... */
104
105 void RFCNB_NBName_To_AName(char *NBName, char *AName)
106
107 { char c, c1, c2;
108   int i;
109
110   for (i=0; i < 16; i++) {
111
112     c1 = NBName[i*2];
113     c2 = NBName[i*2+1];
114
115     c = (char)(((int)c1 - (int)'A') * 16 + ((int)c2 - (int)'A'));
116
117     AName[i] = c;
118
119   }
120
121   AName[i] = 0;   /* Put a null on the end ... */
122
123 }
124
125 /* Print a string of bytes in HEX etc */
126
127 void RFCNB_Print_Hex(FILE *fd, struct RFCNB_Pkt *pkt, int Offset, int Len)
128
129 { char c1, c2, outbuf1[33];
130   unsigned char c;
131   int i, j;
132   struct RFCNB_Pkt *pkt_ptr = pkt;
133   static char Hex_List[17] = "0123456789ABCDEF";
134
135   j = 0;
136
137   /* We only want to print as much as sepcified in Len */
138
139   while (pkt_ptr != NULL) {
140
141     for (i = 0;
142          i < ((Len > (pkt_ptr -> len)?pkt_ptr -> len:Len) - Offset);
143          i++) {
144
145       c = pkt_ptr -> data[i + Offset];
146       c1 = Hex_List[c >> 4];
147       c2 = Hex_List[c & 0xF];
148
149       outbuf1[j++] = c1; outbuf1[j++] = c2;
150
151       if (j == 32){ /* Print and reset */
152         outbuf1[j] = 0;
153         fprintf(fd, "    %s\n", outbuf1);
154         j = 0;
155       }
156
157     }
158
159     Offset = 0;
160     Len = Len - pkt_ptr -> len;   /* Reduce amount by this much */
161     pkt_ptr = pkt_ptr -> next;
162
163   }
164
165   /* Print last lot in the buffer ... */
166
167   if (j > 0) {
168
169     outbuf1[j] = 0;
170     fprintf(fd, "    %s\n", outbuf1);
171
172   }
173
174   fprintf(fd, "\n");
175
176 }
177
178 /* Get a packet of size n */
179
180 struct RFCNB_Pkt *RFCNB_Alloc_Pkt(int n)
181
182 { RFCNB_Pkt *pkt;
183
184   if ((pkt = (struct RFCNB_Pkt *)malloc(sizeof(struct RFCNB_Pkt))) == NULL) {
185
186     RFCNB_errno = RFCNBE_NoSpace;
187     RFCNB_saved_errno = errno;
188     return(NULL);
189
190   }
191
192   pkt -> next = NULL;
193   pkt -> len = n;
194
195   if (n == 0) return(pkt);
196
197   if ((pkt -> data = (char *)malloc(n)) == NULL) {
198
199     RFCNB_errno = RFCNBE_NoSpace;
200     RFCNB_saved_errno = errno;
201     free(pkt);
202     return(NULL);
203
204   }
205
206   return(pkt);
207
208 }
209
210 /* Free up a packet */
211
212 int RFCNB_Free_Pkt(struct RFCNB_Pkt *pkt)
213
214 { struct RFCNB_Pkt *pkt_next; char *data_ptr;
215
216   while (pkt != NULL) {
217
218     pkt_next = pkt -> next;
219
220     data_ptr = pkt -> data;
221
222     if (data_ptr != NULL)
223       free(data_ptr);
224
225     free(pkt);
226
227     pkt = pkt_next;
228
229   }
230
231 }
232
233 /* Print an RFCNB packet */
234
235 void RFCNB_Print_Pkt(FILE *fd, char *dirn, struct RFCNB_Pkt *pkt, int len)
236
237 { char lname[17];
238
239   /* We assume that the first fragment is the RFCNB Header  */
240   /* We should loop through the fragments printing them out */
241
242   fprintf(fd, "RFCNB Pkt %s:", dirn);
243
244   switch (RFCNB_Pkt_Type(pkt -> data)) {
245
246   case RFCNB_SESSION_MESSAGE:
247
248     fprintf(fd, "SESSION MESSAGE: Length = %i\n", RFCNB_Pkt_Len(pkt -> data));
249     RFCNB_Print_Hex(fd, pkt, RFCNB_Pkt_Hdr_Len,
250 #ifdef RFCNB_PRINT_DATA
251                     RFCNB_Pkt_Len(pkt -> data) - RFCNB_Pkt_Hdr_Len
252 #else
253                     40
254 #endif
255             );
256   if (Prot_Print_Routine != 0) { /* Print the rest of the packet */
257
258     Prot_Print_Routine(fd, strcmp(dirn, "sent"), pkt, RFCNB_Pkt_Hdr_Len,
259                        RFCNB_Pkt_Len(pkt -> data) - RFCNB_Pkt_Hdr_Len);
260
261       }
262
263       break;
264
265  case RFCNB_SESSION_REQUEST:
266
267       fprintf(fd, "SESSION REQUEST: Length = %i\n",
268                   RFCNB_Pkt_Len(pkt -> data));
269       RFCNB_NBName_To_AName((char *)(pkt -> data + RFCNB_Pkt_Called_Offset), lname);
270       fprintf(fd, "  Called Name: %s\n", lname);
271       RFCNB_NBName_To_AName((char *)(pkt -> data + RFCNB_Pkt_Calling_Offset), lname);
272       fprintf(fd, "  Calling Name: %s\n", lname);
273
274       break;
275
276  case RFCNB_SESSION_ACK:
277
278       fprintf(fd, "RFCNB SESSION ACK: Length = %i\n",
279                   RFCNB_Pkt_Len(pkt -> data));
280
281       break;
282
283  case RFCNB_SESSION_REJ:
284       fprintf(fd, "RFCNB SESSION REJECT: Length = %i\n",
285                   RFCNB_Pkt_Len(pkt -> data));
286
287       if (RFCNB_Pkt_Len(pkt -> data) < 1) {
288         fprintf(fd, "   Protocol Error, short Reject packet!\n");
289       }
290       else {
291         fprintf(fd, "   Error = %x\n", CVAL(pkt -> data, RFCNB_Pkt_Error_Offset));
292       }
293
294       break;
295
296  case RFCNB_SESSION_RETARGET:
297
298       fprintf(fd, "RFCNB SESSION RETARGET: Length = %i\n",
299                   RFCNB_Pkt_Len(pkt -> data));
300
301       /* Print out the IP address etc and the port? */
302
303       break;
304
305  case RFCNB_SESSION_KEEP_ALIVE:
306
307       fprintf(fd, "RFCNB SESSION KEEP ALIVE: Length = %i\n",
308               RFCNB_Pkt_Len(pkt -> data));
309       break;
310
311     default:
312
313       break;
314   }
315
316 }
317
318 /* Resolve a name into an address */
319
320 int RFCNB_Name_To_IP(char *host, struct in_addr *Dest_IP)
321
322 {
323         lrad_ipaddr_t ipaddr;
324
325         if (ip_hton(host, AF_INET, &ipaddr) < 0) {
326                 /* Try NetBIOS name lookup, how the hell do we do that? */
327
328                 RFCNB_errno = RFCNBE_BadName;   /* Is this right? */
329                 RFCNB_saved_errno = errno;
330                 return(RFCNBE_Bad);
331
332         }
333
334         memcpy(Dest_IP, &ipaddr.ipaddr.ip4addr, sizeof(struct in_addr));
335         return 0;
336 }
337
338 /* Disconnect the TCP connection to the server */
339
340 int RFCNB_Close(int socket)
341
342 {
343
344   close(socket);
345
346   /* If we want to do error recovery, here is where we put it */
347
348   return 0;
349
350 }
351
352 /* Connect to the server specified in the IP address.
353    Not sure how to handle socket options etc.         */
354
355 int RFCNB_IP_Connect(struct in_addr Dest_IP, int port)
356
357 { struct sockaddr_in Socket;
358   int fd;
359
360   /* Create a socket */
361
362   if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { /* Handle the error */
363
364     RFCNB_errno = RFCNBE_BadSocket;
365     RFCNB_saved_errno = errno;
366     return(RFCNBE_Bad);
367     }
368
369   bzero((char *)&Socket, sizeof(Socket));
370   memcpy((char *)&Socket.sin_addr, (char *)&Dest_IP, sizeof(Dest_IP));
371
372   Socket.sin_port = htons(port);
373   Socket.sin_family = PF_INET;
374
375   /* Now connect to the destination */
376
377   if (connect(fd, (struct sockaddr *)&Socket, sizeof(Socket)) < 0) { /* Error */
378
379     close(fd);
380     RFCNB_errno = RFCNBE_ConnectFailed;
381     RFCNB_saved_errno = errno;
382     return(RFCNBE_Bad);
383     }
384
385   return(fd);
386
387 }
388
389 /* handle the details of establishing the RFCNB session with remote
390    end
391
392 */
393
394 int RFCNB_Session_Req(struct RFCNB_Con *con,
395                       char *Called_Name,
396                       char *Calling_Name,
397                       BOOL *redirect,
398                       struct in_addr *Dest_IP,
399                       int * port)
400
401 { char *sess_pkt;
402
403   /* Response packet should be no more than 9 bytes, make 16 jic */
404
405   char resp[16];
406   int len;
407   struct RFCNB_Pkt *pkt, res_pkt;
408
409   /* We build and send the session request, then read the response */
410
411   pkt = RFCNB_Alloc_Pkt(RFCNB_Pkt_Sess_Len);
412
413   if (pkt == NULL) {
414
415     return(RFCNBE_Bad);  /* Leave the error that RFCNB_Alloc_Pkt gives) */
416
417   }
418
419   sess_pkt = pkt -> data;    /* Get pointer to packet proper */
420
421   sess_pkt[RFCNB_Pkt_Type_Offset]  = RFCNB_SESSION_REQUEST;
422   RFCNB_Put_Pkt_Len(sess_pkt, RFCNB_Pkt_Sess_Len-RFCNB_Pkt_Hdr_Len);
423   sess_pkt[RFCNB_Pkt_N1Len_Offset] = 32;
424   sess_pkt[RFCNB_Pkt_N2Len_Offset] = 32;
425
426   RFCNB_CvtPad_Name(Called_Name, (sess_pkt + RFCNB_Pkt_Called_Offset));
427   RFCNB_CvtPad_Name(Calling_Name, (sess_pkt + RFCNB_Pkt_Calling_Offset));
428
429   /* Now send the packet */
430
431 #ifdef RFCNB_DEBUG
432
433   fprintf(stderr, "Sending packet: ");
434
435 #endif
436
437   if ((len = RFCNB_Put_Pkt(con, pkt, RFCNB_Pkt_Sess_Len)) < 0) {
438
439     return(RFCNBE_Bad);       /* Should be able to write that lot ... */
440
441     }
442
443 #ifdef RFCNB_DEBUG
444
445   fprintf(stderr, "Getting packet.\n");
446
447 #endif
448
449   res_pkt.data = resp;
450   res_pkt.len  = sizeof(resp);
451   res_pkt.next = NULL;
452
453   if ((len = RFCNB_Get_Pkt(con, &res_pkt, sizeof(resp))) < 0) {
454
455     return(RFCNBE_Bad);
456
457   }
458
459   /* Now analyze the packet ... */
460
461   switch (RFCNB_Pkt_Type(resp)) {
462
463     case RFCNB_SESSION_REJ:         /* Didnt like us ... too bad */
464
465       /* Why did we get rejected ? */
466
467       switch (CVAL(resp,RFCNB_Pkt_Error_Offset)) {
468
469       case 0x80:
470         RFCNB_errno = RFCNBE_CallRejNLOCN;
471         break;
472       case 0x81:
473         RFCNB_errno = RFCNBE_CallRejNLFCN;
474         break;
475       case 0x82:
476         RFCNB_errno = RFCNBE_CallRejCNNP;
477         break;
478       case 0x83:
479         RFCNB_errno = RFCNBE_CallRejInfRes;
480         break;
481       case 0x8F:
482         RFCNB_errno = RFCNBE_CallRejUnSpec;
483         break;
484       default:
485         RFCNB_errno = RFCNBE_ProtErr;
486         break;
487       }
488
489       return(RFCNBE_Bad);
490       break;
491
492     case RFCNB_SESSION_ACK:        /* Got what we wanted ...      */
493
494       return(0);
495       break;
496
497     case RFCNB_SESSION_RETARGET:   /* Go elsewhere                */
498
499       *redirect = TRUE;       /* Copy port and ip addr       */
500
501       memcpy(Dest_IP, (resp + RFCNB_Pkt_IP_Offset), sizeof(struct in_addr));
502       *port = SVAL(resp, RFCNB_Pkt_Port_Offset);
503
504       return(0);
505       break;
506
507     default:  /* A protocol error */
508
509       RFCNB_errno = RFCNBE_ProtErr;
510       return(RFCNBE_Bad);
511       break;
512     }
513 }