void free_kd(struct kdnode *t) { if(t == NULL) return; else { if( t->loson != NULL) free_kd(t->loson); if( t->hison != NULL) free_kd(t->hison); free(t); } }
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; }
bool rtspcl_setup(struct rtspcl_data *rtspcld, struct key_data **kd, int control_port, int ntp_port, GError **error_r) { struct key_data *rkd = NULL, hds; const char delimiters[] = ";"; char *buf = NULL; char *token, *pc; int rval = false; static char transport_key[] = "Transport"; char transport_value[256]; snprintf(transport_value, sizeof(transport_value), "RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;control_port=%d;timing_port=%d", control_port, ntp_port); hds.key = transport_key; hds.data = transport_value; hds.next = NULL; if (!exec_request(rtspcld, "SETUP", NULL, NULL, 1, &hds, &rkd, error_r)) return false; if (!(rtspcld->session = g_strdup(kd_lookup(rkd, "Session")))) { g_set_error_literal(error_r, rtsp_client_quark(), 0, "no session in response"); goto erexit; } if (!(rtspcld->transport = kd_lookup(rkd, "Transport"))) { g_set_error_literal(error_r, rtsp_client_quark(), 0, "no transport in response"); goto erexit; } buf = g_strdup(rtspcld->transport); token = strtok(buf, delimiters); rtspcld->server_port = 0; rtspcld->control_port = 0; while (token) { if ((pc = strstr(token, "="))) { *pc = 0; if (!strcmp(token,"server_port")) { rtspcld->server_port=atoi(pc + 1); } if (!strcmp(token,"control_port")) { rtspcld->control_port=atoi(pc + 1); } } token = strtok(NULL, delimiters); } if (rtspcld->server_port == 0) { g_set_error_literal(error_r, rtsp_client_quark(), 0, "no server_port in response"); goto erexit; } if (rtspcld->control_port == 0) { g_set_error_literal(error_r, rtsp_client_quark(), 0, "no control_port in response"); goto erexit; } rval = true; erexit: g_free(buf); if (!rval || kd == NULL) { free_kd(rkd); rkd = NULL; } if (kd != NULL) *kd = rkd; return rval; }
/* * send RTSP request, and get response if it's needed * if this gets a success, *kd is allocated or reallocated (if *kd is not NULL) */ bool exec_request(struct rtspcl_data *rtspcld, const char *cmd, const char *content_type, const char *content, int get_response, const struct key_data *hds, struct key_data **kd, GError **error_r) { char line[1024]; char req[1024]; char reql[128]; const char delimiters[] = " "; char *token, *dp; int dsize = 0; int timeout = 5000; // msec unit if (!rtspcld) { g_set_error_literal(error_r, rtsp_client_quark(), 0, "not connected"); return false; } sprintf(req, "%s %s RTSP/1.0\r\nCSeq: %d\r\n", cmd, rtspcld->url, ++rtspcld->cseq ); if ( rtspcld->session != NULL ) { sprintf(reql,"Session: %s\r\n", rtspcld->session ); strncat(req,reql,sizeof(req)); } const struct key_data *hd_iter = hds; while (hd_iter) { sprintf(reql, "%s: %s\r\n", hd_iter->key, hd_iter->data); strncat(req, reql, sizeof(req)); hd_iter = hd_iter->next; } if (content_type && content) { sprintf(reql, "Content-Type: %s\r\nContent-Length: %d\r\n", content_type, (int) strlen(content)); strncat(req,reql,sizeof(req)); } sprintf(reql, "User-Agent: %s\r\n", rtspcld->useragent); strncat(req, reql, sizeof(req)); hd_iter = rtspcld->exthds; while (hd_iter) { sprintf(reql, "%s: %s\r\n", hd_iter->key, hd_iter->data); strncat(req, reql, sizeof(req)); hd_iter = hd_iter->next; } strncat(req, "\r\n", sizeof(req)); if (content_type && content) strncat(req, content, sizeof(req)); if (!tcp_socket_send(rtspcld->tcp_socket, req, strlen(req))) { g_set_error(error_r, rtsp_client_quark(), errno, "write error: %s", g_strerror(errno)); return false; } if (!get_response) return true; if (read_line(rtspcld, line, sizeof(line), timeout) <= 0) { g_set_error_literal(error_r, rtsp_client_quark(), 0, "request failed"); return false; } token = strtok(line, delimiters); token = strtok(NULL, delimiters); if (token == NULL) { g_set_error_literal(error_r, rtsp_client_quark(), 0, "request failed"); return false; } if (strcmp(token, "200") != 0) { g_set_error(error_r, rtsp_client_quark(), 0, "request failed: %s", token); return false; } /* if the caller isn't interested in response headers, put them on the trash, which is freed before returning from this function */ struct key_data *trash = NULL; if (kd == NULL) kd = &trash; struct key_data *cur_kd = *kd; struct key_data *new_kd = NULL; while (read_line(rtspcld, line, sizeof(line), timeout) > 0) { timeout = 1000; // once it started, it shouldn't take a long time if (new_kd != NULL && line[0] == ' ') { const char *j = line; while (*j == ' ') ++j; dsize += strlen(j); new_kd->data = g_realloc(new_kd->data, dsize); strcat(new_kd->data, j); continue; } dp = strstr(line, ":"); if (!dp) { free_kd(*kd); *kd = NULL; g_set_error_literal(error_r, rtsp_client_quark(), 0, "request failed, bad header"); return false; } *dp++ = 0; new_kd = g_new(struct key_data, 1); new_kd->key = g_strdup(line); dsize = strlen(dp) + 1; new_kd->data = g_strdup(dp); new_kd->next = NULL; if (cur_kd == NULL) { cur_kd = *kd = new_kd; } else { cur_kd->next = new_kd; cur_kd = new_kd; } } free_kd(trash); return true; }
static void rtspcl_remove_all_exthds(struct rtspcl_data *rtspcld) { free_kd(rtspcld->exthds); rtspcld->exthds = NULL; }
/* * 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; }