主にAPIを意識した切り分け・・・のつもりだったけど、収集つかなくなって投げた形になってる。
小学生でももっとマシなコード書ける感。
ネットワーク周りの処理が長くなりがちなので、イベント毎にコールバック関数作ってあげて、もう少しすっきりさせてみようと思います。
/* 遠隔rconツール hlrcon v3 v3 ・バインディングで使いやすいようにした(失敗作) v2 ・書き直した usage: hlrcon.exe 123.45.67.89 27015 "pass" command [args...] compile: WIN32 gcc hlrcon.c -lwsock32 -o hlrcon.exe Linux gcc hlcon.c -o hlrcon */ #include <stdio.h> #include <string.h> #ifdef WIN32 #include <winsock.h> #else #include <sys/socket.h> #include <arpa/inet.h> #include <netdb.h> typedef int SOCKET; #endif #undef FD_SETSIZE #define FD_SETSIZE (32) #define BUFFSIZE (0xFFFF >> 2) // チャレンジコード発行時のレスポンスプレフィクス const char CHALLENGE_RCON_PREFIX[] = "\xFF\xFF\xFF\xFF" "challenge rcon "; const int CHALLENGE_RCON_PREFIX_LENGTH = sizeof(CHALLENGE_RCON_PREFIX) - 1; // 不正なチャレンジコードだった場合のレスポンスプレフィクス const char BAD_CHALLENGE_PREFIX[] = "\xFF\xFF\xFF\xFF" "9Bad challenge"; const int BAD_CHALLENGE_PREFIX_LENGTH = sizeof(BAD_CHALLENGE_PREFIX) - 1; // RCONコマンド送信後のレスポンスプレフィクス const char RCON_COMMAND_OK_PREFIX[] = "\xFF\xFF\xFF\xFF" "\x6C"; const int RCON_COMMAND_OK_PREFIX_LENGTH = sizeof(RCON_COMMAND_OK_PREFIX) - 1; enum HLRCON_ERROR_CODE{ HLRCON_OK, HLRCON_COMMAND_OK, HLRCON_CHALLENGE_OK, // ERROR HLRCON_ADDRESS_IS_NULL_ERROR, HLRCON_PASSWORD_IS_NULL_ERROR, HLRCON_COMMAND_IS_NULL_ERROR, HLRCON_RESPONSE_IS_NULL_ERROR, HLRCON_SOCKET_ERROR, HLRCON_HOST_LOOKUP_ERROR, HLRCON_FAILURE_SEND_CHALLENGE_REQUEST, HLRCON_FAILURE_SEND_CHALLENGE_CODE, HLRCON_FAILURE_RECV_CHALLENGE_CODE, HLRCON_CHALLENGE_NG, HLRCON_RECV_ERROR, HLRCON_TIMEOUT_ERROR, HLRCON_UNKNOWN_ERROR, }; // エラーメッセージ変換 const char * hlrconErrorCodeMessage(int code){ switch(code){ case HLRCON_OK: return "OK."; case HLRCON_SOCKET_ERROR: return "Socket error."; case HLRCON_HOST_LOOKUP_ERROR: return "Host lookup error."; case HLRCON_ADDRESS_IS_NULL_ERROR: return "Address is null."; case HLRCON_PASSWORD_IS_NULL_ERROR: return "PASSWORD is null."; case HLRCON_COMMAND_IS_NULL_ERROR: return "COMMAND is null."; case HLRCON_RESPONSE_IS_NULL_ERROR: return "RESSPONSE is null."; case HLRCON_FAILURE_SEND_CHALLENGE_REQUEST: return "Failure to send the challenge request."; case HLRCON_FAILURE_SEND_CHALLENGE_CODE: return "Failure to send the challenge code."; case HLRCON_FAILURE_RECV_CHALLENGE_CODE: return "Failure to recv the challenge code."; case HLRCON_RECV_ERROR: return "Recv error."; case HLRCON_COMMAND_OK: return "Commend OK"; case HLRCON_CHALLENGE_OK: return "Can sent challenge code."; case HLRCON_CHALLENGE_NG: return "Bad challenge code."; case HLRCON_TIMEOUT_ERROR: return "Timeout error."; case HLRCON_UNKNOWN_ERROR: return "Unknown error."; } return "Unknown error."; } // socket close // argv: SOCKET void hlrconSocketClose(SOCKET sock){ #ifdef WIN32 closesocket(sock); WSACleanup(); #else close (sock); #endif } // socket open // ret: SOCKET SOCKET hlrconSocketOpen(){ SOCKET sock; // 通信初期化 #ifdef WIN32 struct WSAData wsaData; WSAStartup(MAKEWORD(2,0), &wsaData); #endif sock = socket(AF_INET, SOCK_DGRAM, 0); return sock; } // convert Host to IP // ret: success:0, error:1 enum HLRCON_ERROR_CODE hlrconHostToIp(const char *srcHost, char *dstIp){ struct hostent *host = gethostbyname(srcHost); if(host == NULL){ #ifdef WIN32 WSACleanup(); #endif return HLRCON_HOST_LOOKUP_ERROR; } strncpy(dstIp, inet_ntoa(*(struct in_addr *)(host->h_addr_list[0])), 16); dstIp[15] = '\0'; return HLRCON_OK; } // RCONレシーバ // レスポンス内容をbufへ記録した後、記録長をrecvsizeへセットする // レスポンス内容により、戻り値は異なる enum HLRCON_ERROR_CODE hlrconRecv(const SOCKET sock, char *buf, const int bufsize, struct sockaddr_in *fromaddr, int *recvsize){ int fromlen = sizeof(struct sockaddr_in); int ret; memset(buf, '\0', bufsize); ret = recvfrom(sock, buf, bufsize, 0, (struct sockaddr *)fromaddr, &fromlen); if(recvsize != NULL){ *recvsize = ret; } if(ret < 0){ return HLRCON_RECV_ERROR; } if(memcmp(buf, CHALLENGE_RCON_PREFIX, CHALLENGE_RCON_PREFIX_LENGTH) == 0){ return HLRCON_CHALLENGE_OK; } if(memcmp(buf, BAD_CHALLENGE_PREFIX, BAD_CHALLENGE_PREFIX_LENGTH) == 0){ return HLRCON_CHALLENGE_NG; } return HLRCON_COMMAND_OK; } // send HLRCON command // argv: etc... // ret: HLRCON_ERROR_CODE enum HLRCON_ERROR_CODE hlrconExecute(const char * address, const unsigned short port, const char * password, const char * command, char *response, const int responseBufferSize){ SOCKET sock; // 非同期ループ int loop = 1; int ret, i; char ip_addr[16]; char challenge_code[128]; struct sockaddr_in fromaddr; struct sockaddr_in toaddr; fd_set fds, readfds; struct timeval timeout; timeout.tv_sec = 5; timeout.tv_usec= 0; if(address == NULL){ return HLRCON_ADDRESS_IS_NULL_ERROR; } if(password == NULL){ return HLRCON_PASSWORD_IS_NULL_ERROR; } if(command == NULL){ return HLRCON_COMMAND_IS_NULL_ERROR; } if(response == NULL){ return HLRCON_RESPONSE_IS_NULL_ERROR; } // ホスト名解決 ret = hlrconHostToIp(address, ip_addr); if(ret != HLRCON_OK){ return ret; } sock = hlrconSocketOpen(); if(!sock){ return HLRCON_SOCKET_ERROR; } // あて先指定 toaddr.sin_family = AF_INET; toaddr.sin_port = htons(port); #ifdef WIN32 toaddr.sin_addr.S_un.S_addr = inet_addr(ip_addr); #else toaddr.sin_addr.s_addr = inet_addr(ip_addr); #endif // 接続 ret = connect(sock,(struct sockaddr *)&toaddr, sizeof(toaddr)); if(ret == -1){ hlrconSocketClose(sock); return HLRCON_SOCKET_ERROR; } ret = sendto(sock, "\xFF\xFF\xFF\xFF challenge rcon\n", 20, 0, (struct sockaddr *)&toaddr, sizeof(toaddr)); if(ret == -1){ hlrconSocketClose(sock); return HLRCON_FAILURE_SEND_CHALLENGE_REQUEST; } // 非同期のためのファイルディスクリプタの設定 FD_ZERO(&readfds); // 初期化 FD_SET(sock, &readfds); // 監視ソケット追加 // ループ処理 while(loop){ memcpy(&fds, &readfds, sizeof(fd_set)); ret = select(sock + 1, &fds, NULL, NULL, &timeout); if(ret == 0){ // timeout return HLRCON_TIMEOUT_ERROR; } if(FD_ISSET(sock, &fds)){ int recvsize; ret = hlrconRecv(sock, response, responseBufferSize, &fromaddr, &recvsize); printf("recv: %s[%d byte]\n", response, recvsize); switch(ret){ case HLRCON_CHALLENGE_OK: // チャレンジコード取得 memset(challenge_code, 0, sizeof(challenge_code)); strncpy(challenge_code, response+CHALLENGE_RCON_PREFIX_LENGTH, sizeof(challenge_code)-1); for(i=0; i<sizeof(challenge_code); ++i){ switch(challenge_code[i]){ case ' ': case '\n': case '\r': case '\0': challenge_code[i] = '\0'; i=sizeof(challenge_code); // 終わり break; } } if(challenge_code[0] == '\0'){ hlrconSocketClose(sock); return HLRCON_FAILURE_RECV_CHALLENGE_CODE; } // チャレンジコードを含めてリクエスト ret = snprintf(response, responseBufferSize, "\xFF\xFF\xFF\xFF rcon %s \"%s\" %s", challenge_code, password, command); response[responseBufferSize - 1] = '\0'; ret = sendto(sock, response, ret + 1, 0, (struct sockaddr *)&toaddr, sizeof(toaddr)); if(ret == -1){ hlrconSocketClose(sock); return HLRCON_FAILURE_SEND_CHALLENGE_CODE; } printf("send: %s[%d byte]\n", response, ret); break; case HLRCON_COMMAND_OK: // コマンド処理結果 loop = 0; // ループ脱出 response[responseBufferSize - 1] = '\0'; break; default: // 何かしらのエラー hlrconSocketClose(sock); return ret; } } } hlrconSocketClose(sock); return HLRCON_OK; } // メイン処理 int main(int argc, char **argv){ char buf[BUFFSIZE]; char command_buf[2048]={0}; int i, port; int check; enum HLRCON_ERROR_CODE code; // 引数チェック if(argc<4){ printf("Usage: this.exe ADDRESS PORT PASS COMMAND\n"); return 1; } // 引数チェック check = 0; for(i=4;i<argc;i++){ check += strlen(argv[i]) + 1; if(check >= sizeof(command_buf)){ fprintf(stderr, "error:too long a command\n"); return 1; } strcat(command_buf, argv[i]); strcat(command_buf, " "); } command_buf[check] = '\0'; // ' ' -> '\0' port = atoi(argv[2]); if(port < 1 || port > 65535){ fprintf(stderr, "error:port.\n"); return 1; } code = hlrconExecute(argv[1], port, argv[3], command_buf, buf, BUFFSIZE); if(code == HLRCON_OK){ printf("result: %s\n", buf + RCON_COMMAND_OK_PREFIX_LENGTH); return 0; }else{ fprintf(stderr, "%s[code:%d]\n", hlrconErrorCodeMessage(code), code); return 1; } }