3 * UCC (University [of WA] Computer Club) Electronic Accounting System
7 * - Client/Server communication
9 * This file is licenced under the 3-clause BSD Licence. See the file
10 * COPYING for full details.
15 #include <netdb.h> // gethostbyname
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
19 //#include <openssl/sha.h> // SHA1
20 #include <pwd.h> // getpwuids
21 #include <unistd.h> // close/getuid
22 #include <limits.h> // INT_MIN/INT_MAX
24 #include <ctype.h> // isdigit
28 char *ReadLine(int Socket);
29 int sendf(int Socket, const char *Format, ...);
31 // ---------------------
32 // --- Coke Protocol ---
33 // ---------------------
34 int OpenConnection(const char *Host, int Port)
37 struct sockaddr_in serverAddr;
40 host = gethostbyname(Host);
42 fprintf(stderr, "Unable to look up '%s'\n", Host);
46 memset(&serverAddr, 0, sizeof(serverAddr));
48 serverAddr.sin_family = AF_INET; // IPv4
49 // NOTE: I have a suspicion that IPv6 will play sillybuggers with this :)
50 serverAddr.sin_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
51 serverAddr.sin_port = htons(Port);
53 sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
55 fprintf(stderr, "Failed to create socket\n");
59 if( geteuid() == 0 || getuid() == 0 )
62 struct sockaddr_in localAddr;
63 memset(&localAddr, 0, sizeof(localAddr));
64 localAddr.sin_family = AF_INET; // IPv4
66 // Loop through all the top ports until one is avaliable
67 for( i = 512; i < 1024; i ++)
69 localAddr.sin_port = htons(i); // IPv4
70 // Attempt to bind to low port for autoauth
71 if( bind(sock, (struct sockaddr*)&localAddr, sizeof(localAddr)) == 0 )
75 printf("Warning: AUTOAUTH unavaliable\n");
78 if( connect(sock, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0 ) {
79 fprintf(stderr, "Failed to connect to server\n");
83 // We're not authenticated if the connection has just opened
84 gbIsAuthenticated = 0;
89 int Authenticate_AutoAuth(int Socket, const char *Username)
95 // Attempt automatic authentication
96 sendf(Socket, "AUTOAUTH %s\n", Username);
99 buf = ReadLine(Socket);
101 responseCode = atoi(buf);
102 switch( responseCode )
104 case 200: // Autoauth succeeded, return
108 case 401: // Untrusted
109 // fprintf(stderr, "Untrusted host, AUTOAUTH unavaliable\n");
110 ret = RV_PERMISSIONS;
112 case 404: // Bad Username
113 fprintf(stderr, "Bad Username '%s'\n", Username);
114 ret = RV_INVALID_USER;
118 fprintf(stderr, "Unkown response code %i from server\n", responseCode);
120 ret = RV_UNKNOWN_ERROR;
128 int Authenticate_AuthIdent(int Socket)
134 // Attempt automatic authentication
135 sendf(Socket, "AUTHIDENT\n");
137 // Check if it worked
138 buf = ReadLine(Socket);
140 responseCode = atoi(buf);
141 switch( responseCode )
143 case 200: // Autoauth succeeded, return
147 case 401: // Untrusted
148 // fprintf(stderr, "Untrusted host, AUTHIDENT unavaliable\n");
149 ret = RV_PERMISSIONS;
153 fprintf(stderr, "Unkown response code %i from server\n", responseCode);
155 ret = RV_UNKNOWN_RESPONSE;
164 int Authenticate_Password(int Socket, const char *Username)
166 #if USE_PASSWORD_AUTH
171 regmatch_t matches[4];
173 sendf(Socket, "USER %s\n", Username);
174 printf("Using username %s\n", Username);
176 buf = ReadLine(Socket);
179 // Expected format: 100 SALT <something> ...
181 RunRegex(&gSaltRegex, buf, 4, matches, "Malformed server response");
182 responseCode = atoi(buf);
183 if( responseCode != 100 ) {
184 fprintf(stderr, "Unknown repsonse code %i from server\n%s\n", responseCode, buf);
186 return RV_UNKNOWN_ERROR; // ERROR
190 if( memcmp( buf+matches[2].rm_so, "SALT", matches[2].rm_eo - matches[2].rm_so) == 0) {
191 // Store it for later
192 memcpy( salt, buf + matches[3].rm_so, matches[3].rm_eo - matches[3].rm_so );
193 salt[ matches[3].rm_eo - matches[3].rm_so ] = 0;
197 // Give three attempts
198 for( i = 0; i < 3; i ++ )
200 int ofs = strlen(Username)+strlen(salt);
203 char *pass = getpass("Password: ");
206 // Create hash string
207 // <username><salt><hash>
208 strcpy(tmp, Username);
210 SHA1( (unsigned char*)pass, strlen(pass), h );
211 memcpy(tmp+ofs, h, 20);
214 SHA1( (unsigned char*)tmp, ofs+20, h );
215 sprintf(tmpBuf, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
216 h[ 0], h[ 1], h[ 2], h[ 3], h[ 4], h[ 5], h[ 6], h[ 7], h[ 8], h[ 9],
217 h[10], h[11], h[12], h[13], h[14], h[15], h[16], h[17], h[18], h[19]
221 sendf(Socket, "PASS %s\n", tmpBuf);
222 buf = ReadLine(Socket);
224 responseCode = atoi(buf);
226 if( responseCode == 200 ) break;
227 // Bad username/password
228 if( responseCode == 401 ) continue;
230 fprintf(stderr, "Unknown repsonse code %i from server\n%s\n", responseCode, buf);
236 return RV_INVALID_USER; // 2 = Bad Password
240 return RV_INVALID_USER;
245 * \brief Authenticate with the server
246 * \return Boolean Failure
248 int Authenticate(int Socket)
252 if( gbIsAuthenticated ) return 0;
255 pwd = getpwuid( getuid() );
258 if( Authenticate_AutoAuth(Socket, pwd->pw_name) == 0 )
260 else if( Authenticate_AuthIdent(Socket) == 0 )
262 else if( Authenticate_Password(Socket, pwd->pw_name) == 0 )
263 return RV_INVALID_USER;
265 // Set effective user
266 if( gsEffectiveUser ) {
269 sendf(Socket, "SETEUSER %s\n", gsEffectiveUser);
271 buf = ReadLine(Socket);
272 responseCode = atoi(buf);
277 printf("Running as '%s' by '%s'\n", gsEffectiveUser, pwd->pw_name);
281 printf("Only coke members can use `dispense -u`\n");
283 return RV_PERMISSIONS;
286 printf("Invalid user selected\n");
288 return RV_INVALID_USER;
291 fprintf(stderr, "Unkown response code %i from server\n", responseCode);
294 return RV_UNKNOWN_ERROR;
300 gbIsAuthenticated = 1;
305 int GetUserBalance(int Socket)
307 regmatch_t matches[6];
314 if( gsEffectiveUser ) {
315 gsUserName = gsEffectiveUser;
318 pwd = getpwuid( getuid() );
319 gsUserName = strdup(pwd->pw_name);
323 sendf(Socket, "USER_INFO %s\n", gsUserName);
324 buf = ReadLine(Socket);
325 responseCode = atoi(buf);
328 case 202: break; // Ok
331 printf("Invalid user? (USER_INFO failed)\n");
333 return RV_INVALID_USER;
336 fprintf(stderr, "Unkown response code %i from server\n", responseCode);
339 return RV_UNKNOWN_ERROR;
342 RunRegex(&gUserInfoRegex, buf, 6, matches, "Malformed server response");
344 giUserBalance = atoi( buf + matches[4].rm_so );
345 gsUserFlags = strdup( buf + matches[5].rm_so );
353 * \brief Read an item info response from the server
354 * \param Dest Destination for the read item (strings will be on the heap)
356 int ReadItemInfo(int Socket, tItem *Dest)
361 regmatch_t matches[8];
365 buf = ReadLine(Socket);
366 responseCode = atoi(buf);
373 printf("Bad item name\n");
378 fprintf(stderr, "Unknown response from dispense server (Response Code %i)\n%s", responseCode, buf);
380 return RV_UNKNOWN_ERROR;
383 RunRegex(&gItemRegex, buf, 8, matches, "Malformed server response");
385 buf[ matches[3].rm_eo ] = '\0';
386 buf[ matches[5].rm_eo ] = '\0';
387 buf[ matches[7].rm_eo ] = '\0';
389 statusStr = &buf[ matches[5].rm_so ];
391 Dest->ID = atoi( buf + matches[4].rm_so );
393 if( strcmp(statusStr, "avail") == 0 )
395 else if( strcmp(statusStr, "sold") == 0 )
397 else if( strcmp(statusStr, "error") == 0 )
400 fprintf(stderr, "Unknown response from dispense server (status '%s')\n",
402 return RV_UNKNOWN_ERROR;
404 Dest->Price = atoi( buf + matches[6].rm_so );
406 // Hack a little to reduce heap fragmentation
408 char tmpType[strlen(buf + matches[3].rm_so) + 1];
409 char tmpDesc[strlen(buf + matches[7].rm_so) + 1];
410 strcpy(tmpType, buf + matches[3].rm_so);
411 strcpy(tmpDesc, buf + matches[7].rm_so);
413 Dest->Type = strdup( tmpType );
414 Dest->Desc = strdup( tmpDesc );
421 * \brief Fill the item information structure
422 * \return Boolean Failure
424 void PopulateItemList(int Socket)
431 regmatch_t matches[4];
433 // Ask server for stock list
434 send(Socket, "ENUM_ITEMS\n", 11, 0);
435 buf = ReadLine(Socket);
437 //printf("Output: %s\n", buf);
439 responseCode = atoi(buf);
440 if( responseCode != 201 ) {
441 fprintf(stderr, "Unknown response from dispense server (Response Code %i)\n", responseCode);
442 exit(RV_UNKNOWN_ERROR);
450 RunRegex(&gArrayRegex, buf, 4, matches, "Malformed server response");
452 arrayType = &buf[ matches[2].rm_so ]; buf[ matches[2].rm_eo ] = '\0';
453 count = atoi( &buf[ matches[3].rm_so ] );
456 if( strcmp(arrayType, "Items") != 0 ) {
458 fprintf(stderr, "Unexpected array type, expected 'Items', got '%s'\n",
460 exit(RV_UNKNOWN_ERROR);
465 gaItems = malloc( giNumItems * sizeof(tItem) );
467 // Fetch item information
468 for( i = 0; i < giNumItems; i ++ )
470 ReadItemInfo( Socket, &gaItems[i] );
474 buf = ReadLine(Socket);
475 responseCode = atoi(buf);
477 if( responseCode != 200 ) {
478 fprintf(stderr, "Unknown response from dispense server %i\n'%s'",
489 * \brief Get information on an item
490 * \return Boolean Failure
492 int Dispense_ItemInfo(int Socket, const char *Type, int ID)
498 sendf(Socket, "ITEM_INFO %s:%i\n", Type, ID);
500 ret = ReadItemInfo(Socket, &item);
503 printf("%8s:%-2i %2i.%02i %s\n",
505 item.Price/100, item.Price%100,
514 int DispenseCheckPin(int Socket, const char *Username, const char *Pin)
516 int ret, responseCode;
519 if( strlen(Pin) != 4 ) {
520 fprintf(stderr, "Pin format incorrect (not 4 characters long)\n");
524 for( int i = 0; i < 4; i ++ ) {
525 if( !isdigit(Pin[i]) ) {
526 fprintf(stderr, "Pin format incorrect (character %i not a digit)\n", i);
531 sendf(Socket, "PIN_CHECK %s %s\n", Username, Pin);
532 buf = ReadLine(Socket);
534 responseCode = atoi(buf);
535 switch( responseCode )
537 case 200: // Pin correct
542 printf("Pin incorrect\n");
543 ret = RV_INVALID_USER;
546 printf("Not authenticated\n");
547 ret = RV_PERMISSIONS;
550 printf("Only coke members can check accounts other than their own\n");
551 ret = RV_PERMISSIONS;
554 printf("User '%s' not found\n", Username);
555 ret = RV_INVALID_USER;
558 printf("Rate limited or client-server disagree on pin format\n");
559 ret = RV_SERVER_ERROR;
562 printf("Unknown response code %i ('%s')\n", responseCode, buf);
563 ret = RV_UNKNOWN_ERROR;
570 int DispenseSetPin(int Socket, const char *Pin)
572 int ret, responseCode;
575 if( strlen(Pin) != 4 ) {
576 fprintf(stderr, "Pin format incorrect (not 4 characters long)\n");
580 for( int i = 0; i < 4; i ++ ) {
581 if( !isdigit(Pin[i]) ) {
582 fprintf(stderr, "Pin format incorrect (character %i not a digit)\n", i);
587 sendf(Socket, "PIN_SET %s\n", Pin);
588 buf = ReadLine(Socket);
590 responseCode = atoi(buf);
594 printf("Pin Updated\n");
598 printf("Not authenticated\n");
599 ret = RV_PERMISSIONS;
602 printf("Client/server disagreement on pin format\n");
603 ret = RV_SERVER_ERROR;
606 printf("Unknown response code %i ('%s')\n", responseCode, buf);
607 ret = RV_UNKNOWN_ERROR;
614 * \brief Dispense an item
615 * \return Boolean Failure
617 int DispenseItem(int Socket, const char *Type, int ID)
619 int ret, responseCode;
622 // Check for a dry run
624 printf("Dry Run - No action\n");
629 sendf(Socket, "DISPENSE %s:%i\n", Type, ID);
630 buf = ReadLine(Socket);
632 responseCode = atoi(buf);
633 switch( responseCode )
636 printf("Dispense OK\n");
640 printf("Not authenticated\n");
641 ret = RV_PERMISSIONS;
644 printf("Insufficient balance\n");
648 printf("Bad item name\n");
652 printf("Item failed to dispense, is the slot empty?\n");
653 ret = RV_SERVER_ERROR;
656 printf("Dispense not possible (slot empty/permissions)\n");
657 ret = RV_SERVER_ERROR;
660 printf("Unknown response code %i ('%s')\n", responseCode, buf);
661 ret = RV_UNKNOWN_ERROR;
670 * \brief Alter a user's balance
672 int Dispense_AlterBalance(int Socket, const char *Username, int Ammount, const char *Reason)
675 int responseCode, rv = -1;
677 // Check for a dry run
679 printf("Dry Run - No action\n");
685 printf("An amount would be nice\n");
689 sendf(Socket, "ADD %s %i %s\n", Username, Ammount, Reason);
690 buf = ReadLine(Socket);
692 responseCode = atoi(buf);
700 fprintf(stderr, "Insufficient balance\n");
703 case 403: // Not in coke
704 fprintf(stderr, "Permissions error: %s\n", buf+4);
707 case 404: // Unknown user
708 fprintf(stderr, "Unknown user '%s'\n", Username);
709 rv = RV_INVALID_USER;
712 fprintf(stderr, "Unknown response code %i\n'%s'\n", responseCode, buf);
713 rv = RV_UNKNOWN_RESPONSE;
722 * \brief Set a user's balance
723 * \note Only avaliable to dispense admins
725 int Dispense_SetBalance(int Socket, const char *Username, int Balance, const char *Reason)
730 // Check for a dry run
732 printf("Dry Run - No action\n");
736 sendf(Socket, "SET %s %i %s\n", Username, Balance, Reason);
737 buf = ReadLine(Socket);
739 responseCode = atoi(buf);
744 case 200: return 0; // OK
745 case 403: // Not an administrator
746 fprintf(stderr, "You are not an admin\n");
747 return RV_PERMISSIONS;
748 case 404: // Unknown user
749 fprintf(stderr, "Unknown user '%s'\n", Username);
750 return RV_INVALID_USER;
752 fprintf(stderr, "Unknown response code %i\n", responseCode);
753 return RV_UNKNOWN_RESPONSE;
760 * \brief Give money to another user
762 int Dispense_Give(int Socket, const char *Username, int Ammount, const char *Reason)
768 printf("Sorry, you can only give, you can't take.\n");
772 // Fast return on zero
774 printf("Are you actually going to give any?\n");
778 // Check for a dry run
780 printf("Dry Run - No action\n");
784 sendf(Socket, "GIVE %s %i %s\n", Username, Ammount, Reason);
786 buf = ReadLine(Socket);
787 responseCode = atoi(buf);
792 printf("Give succeeded\n");
793 return RV_SUCCESS; // OK
796 fprintf(stderr, "Insufficient balance\n");
799 case 404: // Unknown user
800 fprintf(stderr, "Unknown user '%s'\n", Username);
801 return RV_INVALID_USER;
804 fprintf(stderr, "Unknown response code %i\n", responseCode);
805 return RV_UNKNOWN_RESPONSE;
811 int Dispense_Refund(int Socket, const char *Username, const char *Item, int PriceOverride)
814 int responseCode, ret = -1;
817 if( RunRegex(&gUserItemIdentRegex, Item, 0, NULL, NULL) != 0 )
819 fprintf(stderr, "Error: Invalid item ID passed (should be <type>:<num>)\n");
823 // Check username (quick)
824 if( strchr(Username, ' ') || strchr(Username, '\n') )
826 fprintf(stderr, "Error: Username is invalid (no spaces or newlines please)\n");
831 sendf(Socket, "REFUND %s %s %i\n", Username, Item, PriceOverride);
833 buf = ReadLine(Socket);
834 responseCode = atoi(buf);
838 Dispense_ShowUser(Socket, Username); // Show destination account
842 fprintf(stderr, "Refund access is only avaliable to coke members\n");
843 ret = RV_PERMISSIONS;
846 fprintf(stderr, "Unknown user '%s' passed\n", Username);
847 ret = RV_INVALID_USER;
850 fprintf(stderr, "Invalid item '%s' passed\n", Item);
854 fprintf(stderr, "Unknown response from server %i\n%s\n", responseCode, buf);
863 * \brief Donate money to the club
865 int Dispense_Donate(int Socket, int Ammount, const char *Reason)
871 printf("Sorry, you can only give, you can't take.\n");
875 // Fast return on zero
877 printf("Are you actually going to give any?\n");
881 // Check for a dry run
883 printf("Dry Run - No action\n");
887 sendf(Socket, "DONATE %i %s\n", Ammount, Reason);
888 buf = ReadLine(Socket);
890 responseCode = atoi(buf);
895 case 200: return 0; // OK
898 fprintf(stderr, "Insufficient balance\n");
902 fprintf(stderr, "Unknown response code %i\n", responseCode);
910 * \brief Enumerate users
912 int Dispense_EnumUsers(int Socket)
917 regmatch_t matches[4];
919 if( giMinimumBalance != INT_MIN ) {
920 if( giMaximumBalance != INT_MAX ) {
921 sendf(Socket, "ENUM_USERS min_balance:%i max_balance:%i\n", giMinimumBalance, giMaximumBalance);
924 sendf(Socket, "ENUM_USERS min_balance:%i\n", giMinimumBalance);
928 if( giMaximumBalance != INT_MAX ) {
929 sendf(Socket, "ENUM_USERS max_balance:%i\n", giMaximumBalance);
932 sendf(Socket, "ENUM_USERS\n");
935 buf = ReadLine(Socket);
936 responseCode = atoi(buf);
940 case 201: break; // Ok, length follows
943 fprintf(stderr, "Unknown response code %i\n%s\n", responseCode, buf);
948 // Get count (not actually used)
949 RunRegex(&gArrayRegex, buf, 4, matches, "Malformed server response");
950 nUsers = atoi( buf + matches[3].rm_so );
951 printf("%i users returned\n", nUsers);
956 // Read returned users
958 buf = ReadLine(Socket);
959 responseCode = atoi(buf);
961 if( responseCode != 202 ) break;
965 } while(responseCode == 202);
967 // Check final response
968 if( responseCode != 200 ) {
969 fprintf(stderr, "Unknown response code %i\n%s\n", responseCode, buf);
979 int Dispense_ShowUser(int Socket, const char *Username)
982 int responseCode, ret;
984 sendf(Socket, "USER_INFO %s\n", Username);
985 buf = ReadLine(Socket);
987 responseCode = atoi(buf);
997 printf("Unknown user '%s'\n", Username);
1002 fprintf(stderr, "Unknown response code %i '%s'\n", responseCode, buf);
1012 void _PrintUserLine(const char *Line)
1014 regmatch_t matches[6];
1017 RunRegex(&gUserInfoRegex, Line, 6, matches, "Malformed server response");
1022 int usernameLen = matches[3].rm_eo - matches[3].rm_so;
1023 char username[usernameLen + 1];
1024 int flagsLen = matches[5].rm_eo - matches[5].rm_so;
1025 char flags[flagsLen + 1];
1027 memcpy(username, Line + matches[3].rm_so, usernameLen);
1028 username[usernameLen] = '\0';
1029 memcpy(flags, Line + matches[5].rm_so, flagsLen);
1030 flags[flagsLen] = '\0';
1032 bal = atoi(Line + matches[4].rm_so);
1033 printf("%-15s: $%8.02f (%s)\n", username, ((float)bal)/100, flags);
1037 int Dispense_AddUser(int Socket, const char *Username)
1040 int responseCode, ret;
1042 // Check for a dry run
1044 printf("Dry Run - No action\n");
1048 sendf(Socket, "USER_ADD %s\n", Username);
1050 buf = ReadLine(Socket);
1051 responseCode = atoi(buf);
1053 switch(responseCode)
1056 printf("User '%s' added\n", Username);
1061 printf("Only wheel can add users\n");
1066 printf("User '%s' already exists\n", Username);
1071 fprintf(stderr, "Unknown response code %i '%s'\n", responseCode, buf);
1081 int Dispense_SetUserType(int Socket, const char *Username, const char *TypeString, const char *Reason)
1084 int responseCode, ret;
1086 // Check for a dry run
1088 printf("Dry Run - No action\n");
1092 // TODO: Pre-validate the string
1094 sendf(Socket, "USER_FLAGS %s %s %s\n", Username, TypeString, Reason);
1096 buf = ReadLine(Socket);
1097 responseCode = atoi(buf);
1099 switch(responseCode)
1102 printf("User '%s' updated\n", Username);
1107 printf("Only dispense admins can modify users\n");
1108 ret = RV_PERMISSIONS;
1112 printf("User '%s' does not exist\n", Username);
1113 ret = RV_INVALID_USER;
1117 printf("Flag string is invalid\n");
1122 fprintf(stderr, "Unknown response code %i '%s'\n", responseCode, buf);
1123 ret = RV_UNKNOWN_RESPONSE;
1132 int Dispense_SetItem(int Socket, const char *Type, int ID, int NewPrice, const char *NewName)
1135 int responseCode, ret;
1137 // Check for a dry run
1139 printf("Dry Run - No action\n");
1143 sendf(Socket, "UPDATE_ITEM %s:%i %i %s\n", Type, ID, NewPrice, NewName);
1145 buf = ReadLine(Socket);
1146 responseCode = atoi(buf);
1148 switch(responseCode)
1151 printf("Item %s:%i updated\n", Type, ID);
1156 printf("Only coke members can modify the slots\n");
1157 ret = RV_PERMISSIONS;
1161 printf("Invalid item passed\n");
1166 fprintf(stderr, "Unknown response code %i '%s'\n", responseCode, buf);
1179 char *ReadLine(int Socket)
1181 static char buf[BUFSIZ];
1182 static int bufPos = 0;
1183 static int bufValid = 0;
1185 char *newline = NULL;
1187 char *ret = malloc(10);
1189 #if DEBUG_TRACE_SERVER
1190 printf("ReadLine: ");
1202 len = recv(Socket, buf+bufPos, BUFSIZ-1-bufPos, 0);
1205 return strdup("599 Client Connection Error\n");
1208 buf[bufPos+len] = '\0';
1210 newline = strchr( buf+bufPos, '\n' );
1215 retLen += strlen(buf+bufPos);
1216 ret = realloc(ret, retLen + 1);
1217 strcat( ret, buf+bufPos );
1220 int newLen = newline - (buf+bufPos) + 1;
1221 bufValid = len - newLen;
1224 if( len + bufPos == BUFSIZ - 1 ) bufPos = 0;
1228 #if DEBUG_TRACE_SERVER
1229 printf("%i '%s'\n", retLen, ret);
1235 int sendf(int Socket, const char *Format, ...)
1240 va_start(args, Format);
1241 len = vsnprintf(NULL, 0, Format, args);
1246 va_start(args, Format);
1247 vsnprintf(buf, len+1, Format, args);
1250 #if DEBUG_TRACE_SERVER
1251 printf("sendf: %s", buf);
1254 return send(Socket, buf, len, 0);