int rtspcl_add_exthds(rtspcl_t *p, char *key, char *data) { int i=0; rtspcl_data_t *rtspcld; if(!p) return -1; rtspcld=(rtspcl_data_t *)p; if(!rtspcld->exthds){ if(realloc_memory((void*)&rtspcld->exthds, 17*sizeof(key_data_t),__func__)) return -1; }else{ i=0; while(rtspcld->exthds[i].key) { if(rtspcld->exthds[i].key[0]==0xff) break; i++; } if(i && i%16==0 && rtspcld->exthds[i].key[0]!=0xff){ if(realloc_memory((void*)&rtspcld->exthds,(16*((i%16)+1)+1)*sizeof(key_data_t),__func__)) return -1; memset(rtspcld->exthds+16*(i/16),0,17*sizeof(key_data_t)); } } if(realloc_memory((void*)&rtspcld->exthds[i].key,strlen(key),__func__)) return -1; strcpy((char*)rtspcld->exthds[i].key,key); if(realloc_memory((void*)&rtspcld->exthds[i].data,strlen(data),__func__)) return -1; strcpy((char*)rtspcld->exthds[i].data,data); rtspcld->exthds[i+1].key=NULL; return 0; }
int raopcl_send_sample(raopcl_t *p, u_int8_t *sample, int count ) { int rval=-1; u_int16_t len; u_int8_t header[] = { 0x24, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; const int header_size=sizeof(header); raopcl_data_t *raopcld; if(!p) return -1; raopcld=(raopcl_data_t *)p; if(realloc_memory((void**)&raopcld->data, count+header_size+16, __func__)) goto erexit; memcpy(raopcld->data,header,header_size); len=count+header_size-4; raopcld->data[2]=len>>8; raopcld->data[3]=len&0xff; memcpy(raopcld->data+header_size,sample,count); raop_encrypt(raopcld, raopcld->data+header_size, count); len=count+header_size; raopcld->wblk_remsize=count+header_size; raopcld->wblk_wsize=0; if(set_fd_event(raopcld->sfd, RAOP_FD_READ|RAOP_FD_WRITE, fd_event_callback,(void*)raopcld)) goto erexit; rval=0; erexit: return rval; }
/** * @brief This constructor sets up a basic cleared table of * width/height. * * @param width [in]; The table width. * @param height [in]; The table height. */ CompactCacheTable (int width, int height) { _width = 0; _height = 0; _data = NULL; _minx = 0; _miny = 0; _maxx = 0; _maxy = 0; realloc_memory(width, height); };
/** * @brief This constructor sets up a basic cleared table of * specified x and y bounds. * * @param minx [in]; The lower x bound. * @param miny [in]; The lower y bound. * @param maxx [in]; The upper x bound. * @param maxy [in]; The upper y bound. */ CompactCacheTable (int minx, int miny, int maxx, int maxy) { _width = 0; _height = 0; _minx = 0; _miny = 0; _maxx = 0; _maxy = 0; _data = NULL; realloc_memory(minx, miny, maxx, maxy); };
int raopcl_connect(raopcl_t *p, char *host,u_int16_t destport) { u_int8_t buf[4+8+16]; char sid[16]; char sci[24]; char *sac=NULL,*key=NULL,*iv=NULL; char sdp[1024]; int rval=-1; key_data_t *setup_kd=NULL; char *aj, *token, *pc; const char delimiters[] = ";"; u_int8_t rsakey[512]; int i; raopcl_data_t *raopcld; if(!p) return -1; raopcld=(raopcl_data_t *)p; RAND_bytes(buf, sizeof(buf)); sprintf(sid, "%d", *((u_int32_t*)buf)); sprintf(sci, "%08x%08x",*((u_int32_t*)(buf+4)),*((u_int32_t*)(buf+8))); base64_encode(buf+12,16,&sac); if(!(raopcld->rtspcl=rtspcl_open())) goto erexit; if(rtspcl_set_useragent(raopcld->rtspcl,"iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)")) goto erexit; if(rtspcl_add_exthds(raopcld->rtspcl,"Client-Instance", sci)) goto erexit; if(rtspcl_connect(raopcld->rtspcl, host, destport, sid)) goto erexit; i=rsa_encrypt(raopcld->key,16,rsakey); base64_encode(rsakey,i,&key); remove_char_from_string(key,'='); base64_encode(raopcld->iv,16,&iv); remove_char_from_string(iv,'='); sprintf(sdp, "v=0\r\n" "o=iTunes %s 0 IN IP4 %s\r\n" "s=iTunes\r\n" "c=IN IP4 %s\r\n" "t=0 0\r\n" "m=audio 0 RTP/AVP 96\r\n" "a=rtpmap:96 AppleLossless\r\n" "a=fmtp:96 4096 0 16 40 10 14 2 255 0 0 44100\r\n" "a=rsaaeskey:%s\r\n" "a=aesiv:%s\r\n", sid, rtspcl_local_ip(raopcld->rtspcl), host, key, iv); remove_char_from_string(sac,'='); if(rtspcl_add_exthds(raopcld->rtspcl, "Apple-Challenge", sac)) goto erexit; if(rtspcl_annouce_sdp(raopcld->rtspcl, sdp)) goto erexit; if(rtspcl_mark_del_exthds(raopcld->rtspcl, "Apple-Challenge")) goto erexit; if(rtspcl_setup(raopcld->rtspcl, &setup_kd)) goto erexit; if(!(aj=kd_lookup(setup_kd,"Audio-Jack-Status"))) { ERRMSG("%s: Audio-Jack-Status is missing\n",__func__); goto erexit; } token=strtok(aj,delimiters); while(token){ if((pc=strstr(token,"="))){ *pc=0; if(!strcmp(token,"type") && !strcmp(pc+1,"digital")){ raopcld->ajtype=JACK_TYPE_DIGITAL; } }else{ if(!strcmp(token,"connected")){ raopcld->ajstatus=JACK_STATUS_CONNECTED; } } token=strtok(NULL,delimiters); } if(rtspcl_record(raopcld->rtspcl)) goto erexit; // keep host address and port information if(realloc_memory((void**)&raopcld->addr,strlen(host)+1,__func__)) goto erexit; strcpy(raopcld->addr,host); raopcld->rtsp_port=destport; if(raopcl_stream_connect(raopcld)) goto erexit; rval=0; erexit: if(sac) free(sac); if(key) free(key); if(iv) free(iv); free_kd(setup_kd); return rval; }
/* * send RTSP request, and get responce if it's needed * if this gets a success, *kd is allocated or reallocated (if *kd is not NULL) */ static int exec_request(rtspcl_data_t *rtspcld, char *cmd, char *content_type, char *content, int length, int get_response, key_data_t *hds, key_data_t **kd, char* url) { char line[2048]; char req[1024]; char reql[128]; const char delimiters[] = " "; char *token,*dp; int i,j,dsize,rval,totallength; int timeout=5000; // msec unit if(!rtspcld) return -1; if(url==NULL) { sprintf(req, "%s %s RTSP/1.0\r\n",cmd,rtspcld->url); } else { sprintf(req, "%s %s RTSP/1.0\r\n",cmd,url); } i=0; while( hds && hds[i].key != NULL ) { sprintf(reql,"%s: %s\r\n", hds[i].key, hds[i].data); strncat(req,reql,sizeof(req)); i++; } if( content_type && content) { if(!length) { sprintf(reql, "Content-Type: %s\r\nContent-Length: %d\r\n", content_type, (int)strlen(content)); } else { sprintf(reql, "Content-Type: %s\r\nContent-Length: %d\r\n", content_type, length); } strncat(req,reql,sizeof(req)); } sprintf(reql,"CSeq: %d\r\n",++rtspcld->cseq); strncat(req,reql,sizeof(req)); sprintf(reql, "User-Agent: %s\r\n", rtspcld->useragent ); strncat(req,reql,sizeof(req)); i=0; while(rtspcld->exthds && rtspcld->exthds[i].key) { if(rtspcld->exthds[i].key[0]==0xff) {i++;continue;} sprintf(reql,"%s: %s\r\n", rtspcld->exthds[i].key, rtspcld->exthds[i].data); strncat(req,reql,sizeof(req)); i++; } if( rtspcld->session != NULL ) { sprintf(reql,"Session: %s\r\n",rtspcld->session); strncat(req,reql,sizeof(req)); } strncat(req,"\r\n",sizeof(req)); if( content_type && content) { if(!length) { strncat(req,content,sizeof(req)); } else { totallength = strlen(req) + length; memcpy(req+strlen(req),content,length); } } if(!length) { rval=write(rtspcld->fd,req,strlen(req)); DBGMSG("----> %s : write %s\n",__func__, req); if ( rval != strlen(req) ) { ERRMSG("couldn't write request (%d!=%d)\n",(int)rval,(int)strlen(req)); } } else { rval=write(rtspcld->fd,req,totallength); DBGMSG("----> %s : write %s\n",__func__,req); if ( rval != totallength ) { ERRMSG("couldn't write request (%d!=%d)\n",rval,totallength); } } if( !get_response ) return 0; if(read_line(rtspcld->fd,line,sizeof(line),timeout,0)<=0) { if(get_response==1) { ERRMSG("%s: response : %s request failed\n",__func__, line); return -1; } else { return 0; } } token = strtok(line, delimiters); token = strtok(NULL, delimiters); if(token==NULL || strcmp(token,"200")) { if(get_response==1) { ERRMSG("<------ : %s: request failed, error %s\n",__func__,line); return -1; } } else { DBGMSG("<------ : %s: request ok (%s)\n",__func__,token); } i=0; while(read_line(rtspcld->fd,line,sizeof(line),timeout,0)>0){ DBGMSG("<------ : %s\n",line); timeout=1000; // once it started, it shouldn't take a long time if(i%16==0){ if(realloc_memory((void*)kd,(16*(i/16+1)+1)*sizeof(key_data_t),__func__)) return -1; memset(*kd+16*(i/16),0,17*sizeof(key_data_t)); } if(i && line[0]==' '){ for(j=0;j<strlen(line);j++) if(line[j]!=' ') break; dsize+=strlen(line+j); if(realloc_memory((void*)&(*kd)[i].data,dsize,__func__)) return -1; strcat((char*)(*kd)[i].data,line+j); continue; } dp=strstr(line,":"); if(!dp){ ERRMSG("%s: Request failed, bad header\n",__func__); free_kd(*kd); *kd=NULL; return -1; } *dp=0; if(realloc_memory((void*)&(*kd)[i].key,strlen(line)+1,__func__)) return -1; strcpy((char*)(*kd)[i].key,line); dsize=strlen(dp+1)+1; if(realloc_memory((void*)&(*kd)[i].data,dsize,__func__)) return -1; strcpy((char*)(*kd)[i].data,dp+1); i++; if ( strcmp( line, "Audio-Latency" ) == 0 ) { alatency=atoi( dp+2 ); DBGMSG( "saving latency : %d\n", alatency ); } } while(read_line(rtspcld->fd,line,sizeof(line),timeout,0)>0) { //read body DBGMSG("<------ : %s\n",line); } (*kd)[i].key=NULL; return 0; }
int rtspcl_setup(rtspcl_t *p, key_data_t **kd) { key_data_t *rkd=NULL; key_data_t hds[2]; const char delimiters[] = ";"; char *buf=NULL; char *token,*pc; char *temp; int rval=-1; rtspcl_data_t *rtspcld; if(!p) return -1; rtspcld=(rtspcl_data_t *)p; hds[0].key=(uint8_t*)"Transport"; hds[0].data=(uint8_t*)"RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;control_port=6001;timing_port=6002"; hds[1].key=NULL; if(exec_request(rtspcld, "SETUP", NULL, NULL, 0, 1, hds, &rkd, NULL)) return -1; if(!(temp=kd_lookup(rkd, "Session"))){ ERRMSG("%s: no session in response\n",__func__); goto erexit; } DBGMSG("<------- : %s: session:%s\n",__func__,temp); rtspcld->session = (char *) malloc(100); sprintf( rtspcld->session, "%s", trim(temp) ); if(!(rtspcld->transport=kd_lookup(rkd, "Transport"))){ ERRMSG("%s: no transport in responce\n",__func__); goto erexit; } if(realloc_memory((void*)&buf,strlen(rtspcld->transport)+1,__func__)) goto erexit; strcpy(buf,rtspcld->transport); token=strtok(buf,delimiters); rtspcld->server_port=0; while(token) { if((pc=strstr(token,"="))) { *pc=0; if(!strcmp(token,"server_port")) { rtspcld->server_port=atoi(pc+1); DBGMSG( "got server port : %d\n", rtspcld->server_port ); } if(!strcmp(token,"control_port")) { rtspcld->control_port=atoi(pc+1); DBGMSG( "got control port : %d\n", rtspcld->control_port ); } if(!strcmp(token,"timing_port")) { rtspcld->timing_port=atoi(pc+1); DBGMSG( "got timing port : %d\n", rtspcld->timing_port ); } } token=strtok(NULL,delimiters); } if(rtspcld->server_port==0){ ERRMSG("%s: no server_port in response\n",__func__); goto erexit; } rval=0; erexit: if(buf) free(buf); if(rval) { free_kd(rkd); rkd=NULL; } *kd=rkd; return rval; }