/* Raydium - CQFD Corp. http://raydium.org/ License: GPL - GNU General Public License, see "gpl.txt" file. */ #ifndef DONT_INCLUDE_HEADERS #include "index.h" #else #include "headers/network.h" #endif // Raydium low-level Network API. // TODO: // 1 - protection against duplicate data packets (how ?!) // 01/07/2004: done ? (need more tests) // 2 - per client delays for servers // end of 2004: done too (more test needed) // need proto //char raydium_server_accept_new(struct sockaddr *from, char *name); //void raydium_network_broadcast(char type,char *buff); //void raydium_network_write(struct sockaddr *to, int from, char type,char *buff); //char raydium_network_netcall_add(void *ptr, int type, char tcp); int raydium_network_propag_find(int type) { int i; for(i=0;iraydium_network_propag[i].version) // this propag is newer than our { dec+=sizeof(int); raydium_network_propag[i].version=version; memcpy(raydium_network_propag[i].data,buff+dec,raydium_network_propag[i].size); } } void raydium_network_propag_refresh_id(int i) { char buff[RAYDIUM_NETWORK_PACKET_SIZE]; int dec; if(raydium_network_mode==RAYDIUM_NETWORK_MODE_NONE) return; if(i<0 || i>=RAYDIUM_NETWORK_MAX_PROPAGS || !raydium_network_propag[i].state) { raydium_log("network: ERROR: cannot refresh this propag': invalid id"); return; } raydium_network_propag[i].version++; dec=RAYDIUM_NETWORK_PACKET_OFFSET; memcpy(buff+dec,&raydium_network_propag[i].version,sizeof(int)); dec+=sizeof(int); memcpy(buff+dec,raydium_network_propag[i].data,raydium_network_propag[i].size); raydium_network_write(NULL,raydium_network_uid,raydium_network_propag[i].type,buff); } void raydium_network_propag_refresh(int type) { int i; i=raydium_network_propag_find(type); if(i<0) { raydium_log("network: ERROR: cannot refresh this propag': invalid type"); return; } raydium_network_propag_refresh_id(i); } void raydium_network_propag_refresh_all(void) { int i; for(i=0;i(RAYDIUM_NETWORK_PACKET_SIZE-RAYDIUM_NETWORK_PACKET_OFFSET-RAYDIUM_NETWORK_PROPAG_HEAD)) { raydium_log("network: ERROR: propag' packet is too big for current network size"); return -1; } if(raydium_network_propag_find(type)>=0) { raydium_log("network: ERROR: propag' type already added !"); return -1; } for(i=0;istate=0; } unsigned short raydium_network_queue_tcpid_gen(void) { // not very important.. used for easy client/server tcpid identification //#ifdef RAYDIUM_NETWORK_ONLY_____ static unsigned short gen=100; //#else //static unsigned short gen=1000; //#endif gen++; if(!gen) gen++; return gen; } void raydium_network_queue_tcpid_known_add(int tcpid, int player) { raydium_network_tcpid_i[raydium_network_tcpid_index]=tcpid; // mark this TCP-ID as "known" raydium_network_tcpid_p[raydium_network_tcpid_index]=player; // ... from this player #ifdef DEBUG_NETWORK raydium_log("ACK adding tcpid=%i (player %i) to known packets",tcpid,player); #endif raydium_network_tcpid_index++; if(raydium_network_tcpid_index==RAYDIUM_NETWORK_TX_QUEUE_SIZE) raydium_network_tcpid_index=0; } signed char raydium_network_queue_tcpid_known(unsigned short tcpid, unsigned short player) { int i; if(!tcpid) return 0; for(i=0;i= 0) for(i=0;istate) { raydium_network_queue_element_init(e); raydium_network_stat_lost++; // we're erasing an old waiting packet ! } e->state=1; e->tcpid=tcpid; memcpy(e->packet,packet,RAYDIUM_NETWORK_PACKET_SIZE); e->time=raydium_timecall_clock(); e->retries_left=RAYDIUM_NETWORK_MAX_TRIES; if(to) memcpy(&e->to,to,sizeof(struct sockaddr)); e->to_player=-1; // if server mode, search for destination player if(raydium_network_mode==RAYDIUM_NETWORK_MODE_SERVER) { int i; for(i=0;ito_player=i; } raydium_network_queue_index++; if(raydium_network_queue_index==RAYDIUM_NETWORK_TX_QUEUE_SIZE) raydium_network_queue_index=0; } unsigned long *raydium_network_internal_find_delay_addr(int player) { //if client if(raydium_network_mode==RAYDIUM_NETWORK_MODE_CLIENT) return &raydium_netwok_queue_ack_delay_client; else // (server) { if(player<0 || player>=RAYDIUM_NETWORK_MAX_CLIENTS) { raydium_log("ERROR: server: bad client id in resend queue ! SHOULD NEVER APPEND !"); return NULL; // eeeerk ! } return &raydium_netwok_queue_ack_delay_server[player]; } } void raydium_network_queue_check_time(void) { unsigned long now; unsigned long *delay; raydium_network_Tcp *e; int i; // find current delay for(i=0;ito_player); // TCP style timeout: timeout=estimatedRTT*2 if( now>(e->time+(*delay)*2) || e->time>now ) { // resend packet (and do not add this packet to queue again !) #ifdef DEBUG_NETWORK raydium_log("ACK re-asking: tcpid=%i",e->tcpid); #endif raydium_network_write_notcp=1; raydium_network_write(&e->to,-1,e->packet[0],e->packet); raydium_network_stat_reemitted++; (*delay)*=2; // Karn/Partridge TCP algo if((*delay)/(double)raydium_timecall_clocks_per_sec>RAYDIUM_NETWORK_ACK_DELAY_MAX) { #ifdef DEBUG_NETWORK raydium_log("ACK: slow network ! max ack delay reached"); #endif (*delay)=raydium_timecall_clocks_per_sec*RAYDIUM_NETWORK_ACK_DELAY_MAX; } e->retries_left--; e->time=now; if(e->retries_left==0) { #ifdef DEBUG_NETWORK raydium_log("ACK: packet lost, too many retries: tcpid=%i",e->tcpid); #endif raydium_network_queue_element_init(e); raydium_network_stat_lost++; } } } } void raydium_network_queue_ack_send(unsigned short tcpid, struct sockaddr *to) { char buff[RAYDIUM_NETWORK_PACKET_SIZE]; //unsigned short port; memcpy(buff+RAYDIUM_NETWORK_PACKET_OFFSET,&tcpid,sizeof(tcpid)); raydium_network_write(to,raydium_network_uid,RAYDIUM_NETWORK_PACKET_ACK,buff); //memcpy(&port,&to->sa_data[0],sizeof(port)); //raydium_log("ACK ---> %i.%i.%i.%i:%i",to->sa_data[2],to->sa_data[3],to->sa_data[4],to->sa_data[5],ntohs(port)); } void raydium_network_queue_ack_recv(int type,char *buff) { unsigned short tcpid; int i; raydium_network_Tcp *e; unsigned long now; // TCP style weight average params const float a=0.85; const float b=0.15; memcpy(&tcpid,buff+RAYDIUM_NETWORK_PACKET_OFFSET,sizeof(tcpid)); for(i=0;istate && e->tcpid==tcpid) { // ACK is correct, deleting packet from queue #ifdef DEBUG_NETWORK raydium_log("ACK recv ok: tcpid=%i",tcpid); #endif now=raydium_timecall_clock(); if(e->timeto_player); // Based on original TCP adaptative retransmission algorithm : *(delay)=a * (*delay) + b*(now - e->time); #ifdef DEBUG_NETWORK raydium_log("ACK delay re-eval: %.2f msec (inst=%.2f msec) (client %i)",(*delay)/(double)raydium_timecall_clocks_per_sec*1000,(now - e->time)/(double)raydium_timecall_clocks_per_sec*1000,e->to_player); #endif } raydium_network_queue_element_init(e); return; } } raydium_network_stat_bogus_ack++; #ifdef DEBUG_NETWORK raydium_log("ACK bogus (double ack, probably): tcpid=%i",tcpid); #endif } // ----------------------------------------------------------------------- void raydium_network_player_name(char *str) { #ifndef WIN32 { struct passwd *pn; pn=getpwuid(geteuid()); // ("old" kernels ok ?) strncpy(str,pn->pw_name,RAYDIUM_MAX_NAME_LEN-1); } #else { DWORD s; s=RAYDIUM_MAX_NAME_LEN-1; GetUserName(str,&s); } #endif if(!strlen(str)) gethostname(str,RAYDIUM_MAX_NAME_LEN-1); } signed char raydium_network_set_socket_block_internal(int socket, int block) { int ret=-1; //int flags = fcntl(sock, F_GETFL); // bof... :) #ifndef WIN32 if(!block) block=O_NONBLOCK; else block=0; ret=fcntl(socket, F_SETFL, block); #else if(!block) block=1; else block=0; ret = ioctlsocket(socket, FIONBIO, (unsigned long *)&block); if(ret) ret=-1; #endif if(ret==-1) { raydium_log("ERROR ! network: cannot block/unblock socket"); perror("System"); return(0); } return(1); } signed char raydium_network_set_socket_block(int block) { return raydium_network_set_socket_block_internal(raydium_network_socket,block); } //int raydium_network_get_socket_block(void) //{ //return !( fcntl(raydium_network_socket, F_GETFL) & O_NONBLOCK ); //} int raydium_network_socket_close(int fd) { #ifndef WIN32 return close(fd); #else return closesocket(fd); #endif } // I've no idea why some version of Dev-CPP are losing this value ... #ifndef FD_SETSIZE #define FD_SETSIZE 64 #endif signed char raydium_network_socket_is_readable(int fd) { fd_set readSet; struct timeval timeVal; int selectCount; timeVal.tv_sec=0; timeVal.tv_usec=0; FD_ZERO(&readSet); FD_SET(fd,&readSet); selectCount=select(fd+1,&readSet,NULL,NULL,&timeVal); if (selectCount<0) return 0; // failed if (selectCount==0) return 0; // not readable if (FD_ISSET(fd,&readSet)) return 1; // readable return 0; // ?!! } signed char raydium_network_netcall_add(void *ptr, int type, signed char tcp) { int i; for(i=0;i= 0) for(i=0;iraydium_network_keepalive[i]+RAYDIUM_NETWORK_TIMEOUT) { raydium_log("network: TIMEOUT for client %i (%i sec): %s deleted !",i,RAYDIUM_NETWORK_TIMEOUT,raydium_network_name[i]); raydium_network_client[i]=0; if(raydium_network_on_disconnect) { f=raydium_network_on_disconnect; f(i); } raydium_network_name[i][0]=0; str[RAYDIUM_NETWORK_PACKET_OFFSET]=i; str[RAYDIUM_NETWORK_PACKET_OFFSET+1]=0; raydium_network_broadcast(RAYDIUM_NETWORK_PACKET_INFO_NAME,str); n++; } return n; } return 0; } void raydium_network_init_sub(void) { int i; if(raydium_network_mode) raydium_network_socket_close(raydium_network_socket); raydium_network_uid=-1; raydium_network_mode=RAYDIUM_NETWORK_MODE_NONE; raydium_network_socket=-1; raydium_network_beacon[RAYDIUM_NETWORK_PACKET_OFFSET]=0; raydium_network_beacon_search.active=0; for(i=0;i=0) buff[1]=(char)from; // 256 clients MAX for now. if(raydium_network_write_notcp==0 && raydium_network_queue_is_tcpid(type)) tcpid=raydium_network_queue_tcpid_gen(); if(raydium_network_write_notcp==0) // do not erase tcpid packet's element if it's a re-send memcpy(buff+2,&tcpid,sizeof(unsigned short)); raydium_network_write_notcp=0; raydium_network_stat_tx+=RAYDIUM_NETWORK_PACKET_SIZE; if(raydium_network_mode==RAYDIUM_NETWORK_MODE_CLIENT) ret=send(raydium_network_socket, buff, RAYDIUM_NETWORK_PACKET_SIZE, 0); else if(raydium_network_mode==RAYDIUM_NETWORK_MODE_SERVER) ret=sendto(raydium_network_socket, buff, RAYDIUM_NETWORK_PACKET_SIZE, 0, to, sizeof(struct sockaddr)); raydium_network_timeout_check(); //raydium_log("send ret: %i",ret); if(ret<0) { raydium_log("network: ERROR sending ! (%i)",ret); return; } if(tcpid) { raydium_network_queue_element_add(buff,to); #ifdef DEBUG_NETWORK raydium_log("ACK asking to peer: tcpid=%i type=%i",tcpid,type); #endif } } void raydium_network_broadcast(signed char type,char *buff) { int i; for(i=0;i id already found -> update time -> return for(i=0;i id not found -> test game+version if(version != raydium_network_beacon_search.version || strcmp(app_or_mod,raydium_network_beacon_search.app_or_mod)) return(RAYDIUM_NETWORK_DATA_NONE); // not for us ... name=buff+dec; dec+=(strlen(name)+1); info=buff+dec; dec+=RAYDIUM_NETWORK_BEACON_INFO_MAX_LEN; memcpy(&player_count,buff+dec,sizeof(player_count)); dec+=sizeof(player_count); memcpy(&player_max,buff+dec,sizeof(player_max)); dec+=sizeof(player_max); // true -> search free -> add server if(slot==-1) for(i=0;isin_addr)); strcpy(raydium_network_server_list[slot].info,info); raydium_network_server_list[slot].player_count=player_count; raydium_network_server_list[slot].player_max=player_max; } return(RAYDIUM_NETWORK_DATA_NONE); } raydium_network_netcall_exec(*type,buff); if( raydium_network_mode==RAYDIUM_NETWORK_MODE_SERVER && (*id>=0) && (*id= RAYDIUM_NETWORK_PACKET_SIZE-10) { raydium_log("network: ERROR: cannot set server attributes: packet's too small"); return 0; } player_count=0; player_max=0; id=rand(); dec=RAYDIUM_NETWORK_PACKET_OFFSET; raydium_network_beacon[dec]=1; dec++; memcpy(raydium_network_beacon+dec,&id,sizeof(id)); // server id dec+=sizeof(id); memcpy(raydium_network_beacon+dec,&version,sizeof(version)); dec+=sizeof(version); strcpy(raydium_network_beacon+dec,app_or_mod); dec+=(strlen(app_or_mod)+1); strcpy(raydium_network_beacon+dec,name); dec+=(strlen(name)+1); raydium_network_beacon_info_offset=dec; raydium_network_beacon[dec]=0; // no info yet dec+=RAYDIUM_NETWORK_BEACON_INFO_MAX_LEN; memcpy(raydium_network_beacon+dec,&player_count,sizeof(player_count)); dec+=sizeof(player_count); memcpy(raydium_network_beacon+dec,&player_max,sizeof(player_max)); dec+=sizeof(player_max); raydium_log("network: now broadcasting : '%s' (%s) version %i",name,app_or_mod,version); return 1; } void raydium_network_server_broadcast_info(char *info) { if(raydium_network_mode!=RAYDIUM_NETWORK_MODE_SERVER) { raydium_log("network: ERROR: cannot set server broadcast infos: not a server"); return; } if(strlen(info)last+RAYDIUM_NETWORK_BEACON_DELAY) { int player_count; int player_max; int dec; int i; #ifndef linux struct sockaddr_in sock; #endif player_max=RAYDIUM_NETWORK_MAX_CLIENTS; player_count=0; for(i=0;ih_addr, server_addr->h_length); sock.sin_family=AF_INET; sock.sin_port=htons(RAYDIUM_NETWORK_PORT); ret=connect(raydium_network_socket,(struct sockaddr *)&sock,sizeof(sock)); if(ret) { raydium_log("ERROR ! local UDP socket error (contacting %s)",server); perror("System"); return(0); } raydium_log("network: connecting to %s and waiting UID...",server); raydium_network_set_socket_block(1); setsockopt(raydium_network_socket,SOL_SOCKET,SO_BROADCAST,(char *)&on,sizeof(on)); // needed now, because we use network_write raydium_network_mode=RAYDIUM_NETWORK_MODE_CLIENT; // we need to send request for uid (and send our name) strcpy(str+RAYDIUM_NETWORK_PACKET_OFFSET,raydium_network_name_local); raydium_network_write(NULL,-1,RAYDIUM_NETWORK_PACKET_REQUEST_UID,str); // probably needs timeouts, here ... if (raydium_network_read(&empty,&type,str)!=RAYDIUM_NETWORK_DATA_OK) { raydium_network_mode=RAYDIUM_NETWORK_MODE_NONE; raydium_log("ERROR ! network: cannot connect to server %s",server); perror("System"); raydium_network_socket_close(raydium_network_socket); return(0); } if(type==RAYDIUM_NETWORK_PACKET_ATTRIB_UID) { raydium_network_uid=str[RAYDIUM_NETWORK_PACKET_OFFSET]; raydium_log("network: accepted as client %i",raydium_network_uid); raydium_network_set_socket_block(0); strcpy(raydium_network_connected_server,server); return(1); } if(type==RAYDIUM_NETWORK_PACKET_ERROR_NO_MORE_PLACE) { raydium_network_mode=RAYDIUM_NETWORK_MODE_NONE; raydium_log("ERROR ! network: no more room (server said: %s)",str+RAYDIUM_NETWORK_PACKET_OFFSET); raydium_network_socket_close(raydium_network_socket); return(0); } raydium_network_mode=RAYDIUM_NETWORK_MODE_NONE; raydium_log("ERROR ! network: unknow server message type (%i). abort.",type); raydium_network_socket_close(raydium_network_socket); return(0); } signed char raydium_network_client_discover(char *game,int version) { struct sockaddr_in sock; int on=1; int ret; if(raydium_network_mode!=RAYDIUM_NETWORK_MODE_NONE) { raydium_log("network: ERROR: cannot create discover : already connected"); return 0; } raydium_network_start=time(NULL); raydium_network_socket=socket(AF_INET,RAYDIUM_NETWORK_UDP,0); if(raydium_network_socket==-1) { raydium_log("ERROR ! network: cannot create discover socket"); perror("System"); return(0); } raydium_log("network: discover socket created"); sock.sin_family=AF_INET; sock.sin_addr.s_addr=htonl(INADDR_ANY); sock.sin_port=htons(RAYDIUM_NETWORK_BEACON_PORT); ret=bind(raydium_network_socket,(struct sockaddr *)&sock,sizeof(sock)); if(ret) { raydium_log("ERROR ! network: cannot open discover socket (already used ?)"); perror("System"); return(0); } raydium_network_beacon_search.active=1; strcpy(raydium_network_beacon_search.app_or_mod,game); raydium_network_beacon_search.version=version; raydium_network_mode=RAYDIUM_NETWORK_MODE_DISCOVER; setsockopt(raydium_network_socket,SOL_SOCKET,SO_BROADCAST,(char *)&on,sizeof(on)); raydium_network_set_socket_block(0); raydium_log("network: discover OK: waiting for server beacons with '%s' (version %i)",game,version); return 1; } // return : // -1 : not in discover mode // 0 and more : number of broadcasting servers int raydium_network_discover_numservers(void) { int i; int cpt=0; if(raydium_network_mode!=RAYDIUM_NETWORK_MODE_DISCOVER || !raydium_network_beacon_search.active) return -1; for(i=0;isin_addr)*/,n,name); /* send uid to client */ str[RAYDIUM_NETWORK_PACKET_OFFSET]=n; raydium_network_write(from,-1,RAYDIUM_NETWORK_PACKET_ATTRIB_UID,str); // OnConnect: for(i=0;ih_addr,server->h_length); serv_addr.sin_port = htons(RAYDIUM_NETWORK_INTERNET_TEST_PORT); raydium_network_set_socket_block_internal(sockfd,0); // i'm tired of win32's sockets bad behaviours ... let's trust connect() ... connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); // dont ask me why ... (Dev-CPP's mingw needs it) #ifdef WIN32 #ifndef FD_SETSIZE #define FD_SETSIZE 64 #endif #endif FD_ZERO(&writable); FD_SET(sockfd, &writable); timeout.tv_sec=RAYDIUM_NETWORK_INTERNET_TEST_TIMEOUT; timeout.tv_usec=0; #ifndef ENETUNREACH #define ENETUNREACH WSAENETUNREACH #endif if (select(sockfd + 1, NULL, &writable, NULL, &timeout) <= 0 || errno==ENETUNREACH) { raydium_log("network: cannot contact remote server, no internet connection detected"); raydium_network_socket_close(sockfd); return 0; // not writable } //raydium_log("network: internet link is ok"); raydium_network_socket_close(sockfd); return 1; // writable */ // may use a cache system ? (time-limited ? with an arg ?) return raydium_rayphp_http_test(); } #ifdef linux signed char raydium_network_linux_find_broadcast_interfaces(void) { int fd,len, ifflags; struct sockaddr_in addr; struct ifconf ifconf; struct ifreq *ifreqp, ifreq, ifbuf[RAYDIUM_NETWORK_BROADCAST_INTERFACE_MAX]; char name[RAYDIUM_MAX_NAME_LEN]; char msg[RAYDIUM_MAX_NAME_LEN]; msg[0]=0; raydium_network_broadcast_interface_index=0; if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { raydium_log("network: linux broadcast find interfaces: ERROR"); perror("socket"); return 0; } ifconf.ifc_len = sizeof(ifbuf); ifconf.ifc_buf = (caddr_t)ifbuf; memset((void *)ifbuf, 0, sizeof(ifbuf)); if (ioctl(fd, SIOCGIFCONF, (char *)&ifconf) == -1) { raydium_log("network: linux broadcast find interfaces: ERROR"); perror("ioctl SIOCGIFCONF"); close(fd); return 0; } for (len = 0; len + (int)sizeof(struct ifreq) <= ifconf.ifc_len;) { ifreqp = (struct ifreq *)&ifconf.ifc_buf[len]; len += sizeof(struct ifreq); if (ifreqp->ifr_addr.sa_family != AF_INET) continue; addr = *(struct sockaddr_in *)&ifreqp->ifr_addr; strcpy(name,ifreqp->ifr_name); //printf("\taddress %s\n", inet_ntoa(addr.sin_addr)); ifreq = *ifreqp; if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifreq) == -1) { raydium_log("network: linux broadcast find interfaces: ERROR"); perror("ioctl SIOCGIFFLAGS"); continue; } ifflags = ifreq.ifr_flags; // interface up ? if ((ifflags & IFF_UP) == 0) continue; // running ? if ((ifflags & IFF_RUNNING) == 0) continue; // is loopback ? if ((ifflags & IFF_LOOPBACK) != 0) continue; // can broadcast ? if ((ifflags & IFF_BROADCAST) == 0) continue; ifreq = *ifreqp; if (ioctl(fd, SIOCGIFBRDADDR, (char *)&ifreq) == -1) { perror("ioctl SIOCGIFBRDADDR"); continue; } addr = *(struct sockaddr_in *)&ifreq.ifr_addr; addr.sin_family = AF_INET; addr.sin_port=htons(RAYDIUM_NETWORK_BEACON_PORT); memcpy( &raydium_network_broadcast_interfaces[raydium_network_broadcast_interface_index], &addr,sizeof(addr)); // broadcast address strcat(msg,name); strcat(msg," "); raydium_network_broadcast_interface_index++; } close(fd); raydium_log("network: linux broadcast interface(s): %s",msg); return 1; } #endif