use new RCSID macro to prevent Id keyword from being optimized out
[freeradius.git] / src / modules / rlm_smb / session.c
1 /* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
2
3    Version 1.0
4    Session 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 int RFCNB_errno = 0;
33 int RFCNB_saved_errno = 0;
34 #define RFCNB_ERRNO
35
36 #include "std-includes.h"
37
38 #ifdef HAVE_NETINET_TCP_H
39 #include <netinet/tcp.h>
40 #endif
41
42 #include "rfcnb-priv.h"
43 #include "rfcnb-util.h"
44
45 int RFCNB_Stats[RFCNB_MAX_STATS];
46
47 void (*Prot_Print_Routine)() = NULL;      /* Pointer to print routine */
48
49 /* Set up a session with a remote name. We are passed Called_Name as a
50    string which we convert to a NetBIOS name, ie space terminated, up to
51    16 characters only if we need to. If Called_Address is not empty, then
52    we use it to connect to the remote end, but put in Called_Name ... Called
53    Address can be a DNS based name, or a TCP/IP address ...
54 */
55
56 void *RFCNB_Call(char *Called_Name, char *Calling_Name, char *Called_Address,
57                  int port)
58
59 { struct RFCNB_Con *con;
60   struct in_addr Dest_IP;
61   int Client;
62   BOOL redirect; struct redirect_addr *redir_addr;
63   char *Service_Address;
64
65   /* Now, we really should look up the port in /etc/services ... */
66
67   if (port == 0) port = RFCNB_Default_Port;
68
69   /* Create a connection structure first */
70
71   if ((con = (struct RFCNB_Con *)malloc(sizeof(struct RFCNB_Con))) == NULL) { /* Error in size */
72
73     RFCNB_errno = RFCNBE_NoSpace;
74     RFCNB_saved_errno = errno;
75     return(NULL);
76
77     }
78
79   con -> fd = -0;             /* no descriptor yet */
80   con -> rfc_errno = 0;           /* no error yet */
81   con -> timeout = 0;         /* no timeout   */
82   con -> redirects = 0;
83   con -> redirect_list = NULL; /* Fix bug still in version 0.50 */
84
85   /* Resolve that name into an IP address */
86
87   Service_Address = Called_Name;
88   if (strcmp(Called_Address, "") != 0) { /* If the Called Address = "" */
89     Service_Address = Called_Address;
90   }
91
92   if ((errno = RFCNB_Name_To_IP(Service_Address, &Dest_IP)) < 0) { /* Error */
93
94     /* No need to modify RFCNB_errno as it was done by RFCNB_Name_To_IP */
95
96     return(NULL);
97
98     }
99
100   /* Now connect to the remote end */
101
102   redirect = TRUE;     /* Fudge this one so we go once through */
103
104   while (redirect) {   /* Connect and get session info etc */
105
106     redirect = FALSE;  /* Assume all OK */
107
108     /* Build the redirect info. First one is first addr called */
109     /* And tack it onto the list of addresses we called        */
110
111     if ((redir_addr = (struct redirect_addr *)malloc(sizeof(struct redirect_addr))) == NULL) { /* Could not get space */
112
113       RFCNB_errno = RFCNBE_NoSpace;
114       RFCNB_saved_errno = errno;
115       return(NULL);
116
117     }
118
119     memcpy((char *)&(redir_addr -> ip_addr), (char *)&Dest_IP, sizeof(Dest_IP));
120     redir_addr -> port = port;
121     redir_addr -> next = NULL;
122
123     if (con -> redirect_list == NULL) { /* Stick on head */
124
125       con -> redirect_list = con -> last_addr = redir_addr;
126
127     } else {
128
129       con -> last_addr -> next = redir_addr;
130       con -> last_addr = redir_addr;
131
132     }
133
134     /* Now, make that connection */
135
136     if ((Client = RFCNB_IP_Connect(Dest_IP, port)) < 0) { /* Error */
137
138       /* No need to modify RFCNB_errno as it was done by RFCNB_IP_Connect */
139
140       return(NULL);
141
142       }
143
144     con -> fd = Client;
145
146     /* Now send and handle the RFCNB session request              */
147     /* If we get a redirect, we will comeback with redirect true
148        and a new IP address in DEST_IP                            */
149
150     if ((errno = RFCNB_Session_Req(con,
151                                    Called_Name,
152                                    Calling_Name,
153                                    &redirect, &Dest_IP, &port)) < 0) {
154
155       /* No need to modify RFCNB_errno as it was done by RFCNB_Session.. */
156
157       return(NULL);
158
159       }
160
161     if (redirect) {
162
163       /* We have to close the connection, and then try again */
164
165       (con -> redirects)++;
166
167       RFCNB_Close(con -> fd);  /* Close it */
168
169       }
170     }
171
172   return(con);
173
174 }
175
176 /* We send a packet to the other end ... for the moment, we treat the
177    data as a series of pointers to blocks of data ... we should check the
178    length ... */
179
180 int RFCNB_Send(struct RFCNB_Con *Con_Handle, struct RFCNB_Pkt *udata, int Length)
181
182 { struct RFCNB_Pkt *pkt; char *hdr;
183   int len;
184
185   /* Plug in the header and send the data */
186
187   pkt = RFCNB_Alloc_Pkt(RFCNB_Pkt_Hdr_Len);
188
189   if (pkt == NULL) {
190
191     RFCNB_errno = RFCNBE_NoSpace;
192     RFCNB_saved_errno = errno;
193     return(RFCNBE_Bad);
194
195   }
196
197   pkt -> next = udata;   /* The user data we want to send */
198
199   hdr = pkt -> data;
200
201   /* Following crap is for portability across multiple UNIX machines */
202
203   *(hdr + RFCNB_Pkt_Type_Offset)  = RFCNB_SESSION_MESSAGE;
204   RFCNB_Put_Pkt_Len(hdr, Length);
205
206 #ifdef RFCNB_DEBUG
207
208   fprintf(stderr, "Sending packet: ");
209
210 #endif
211
212   if ((len = RFCNB_Put_Pkt(Con_Handle, pkt, Length + RFCNB_Pkt_Hdr_Len)) < 0) {
213
214     /* No need to change RFCNB_errno as it was done by put_pkt ...     */
215
216     return(RFCNBE_Bad);   /* Should be able to write that lot ... */
217
218   }
219
220   /* Now we have sent that lot, let's get rid of the RFCNB Header and return */
221
222   pkt -> next = NULL;
223
224   RFCNB_Free_Pkt(pkt);
225
226   return(len);
227
228 }
229
230 /* We pick up a message from the internet ... We have to worry about
231    non-message packets ...                                           */
232
233 int RFCNB_Recv(void *con_Handle, struct RFCNB_Pkt *Data, int Length)
234
235 {
236   struct RFCNB_Pkt *pkt;
237   int ret_len;
238
239   if (con_Handle == NULL){
240
241     RFCNB_errno = RFCNBE_BadHandle;
242     RFCNB_saved_errno = errno;
243     return(RFCNBE_Bad);
244
245   }
246
247   /* Now get a packet from below. We allocate a header first */
248
249   /* Plug in the header and send the data */
250
251   pkt = RFCNB_Alloc_Pkt(RFCNB_Pkt_Hdr_Len);
252
253   if (pkt == NULL) {
254
255     RFCNB_errno = RFCNBE_NoSpace;
256     RFCNB_saved_errno = errno;
257     return(RFCNBE_Bad);
258
259   }
260
261   pkt -> next = Data;  /* Plug in the data portion */
262
263   if ((ret_len = RFCNB_Get_Pkt(con_Handle, pkt, Length + RFCNB_Pkt_Hdr_Len)) < 0) {
264
265 #ifdef RFCNB_DEBUG
266     fprintf(stderr, "Bad packet return in RFCNB_Recv... \n");
267 #endif
268
269     return(RFCNBE_Bad);
270
271   }
272
273   /* We should check that we go a message and not a keep alive */
274
275   pkt -> next = NULL;
276
277   RFCNB_Free_Pkt(pkt);
278
279   return(ret_len);
280
281 }
282
283 /* We just disconnect from the other end, as there is nothing in the RFCNB */
284 /* protocol that specifies any exchange as far as I can see                */
285
286 int RFCNB_Hangup(struct RFCNB_Con *con_Handle)
287
288 {
289
290   if (con_Handle != NULL) {
291     RFCNB_Close(con_Handle -> fd);  /* Could this fail? */
292     free(con_Handle);
293   }
294
295   return 0;
296
297
298 }
299
300 /* Set TCP_NODELAY on the socket                                          */
301
302 int RFCNB_Set_Sock_NoDelay(struct RFCNB_Con *con_Handle, BOOL yn)
303
304 {
305
306   return(setsockopt(con_Handle -> fd, IPPROTO_TCP, TCP_NODELAY,
307                     (char *)&yn, sizeof(yn)));
308
309 }
310
311
312 /* Listen for a connection on a port???, when                             */
313 /* the connection comes in, we return with the connection                 */
314
315 void *RFCNB_Listen()
316
317 {
318
319 }
320
321 /* Pick up the last error response as a string, hmmm, this routine should */
322 /* have been different ...                                                */
323
324 void RFCNB_Get_Error(char *buffer, int buf_len)
325
326 {
327
328   if (RFCNB_saved_errno <= 0) {
329     sprintf(buffer, "%s", RFCNB_Error_Strings[RFCNB_errno]);
330   }
331   else {
332     sprintf(buffer, "%s\n\terrno:%s", RFCNB_Error_Strings[RFCNB_errno],
333             strerror(RFCNB_saved_errno));
334   }
335
336 }
337
338 /* Pick up the last error response and returns as a code                 */
339
340 int RFCNB_Get_Last_Error()
341
342 {
343
344   return(RFCNB_errno);
345
346 }
347
348 /* Pick up saved errno as well */
349
350 int RFCNB_Get_Last_Errno()
351
352 {
353
354   return(RFCNB_saved_errno);
355
356 }
357
358 /* Pick up the last error response and return in string ...             */
359
360 int RFCNB_Get_Error_Msg(int code, char *msg_buf, int len)
361
362 {
363
364   strncpy(msg_buf, RFCNB_Error_Strings[abs(code)], len);
365
366 }
367
368 /* Register a higher level protocol print routine */
369
370 void RFCNB_Register_Print_Routine(void (*fn)())
371
372 {
373
374   Prot_Print_Routine = fn;
375
376 }