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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors