1 /*********************************************************************************
\r
3 * --------------------------------------------------------------------------------
\r
5 * This file is part of MiMic
\r
6 * Copyright (C)2011 Ryo Iizuka
\r
8 * MiMic is free software: you can redistribute it and/or modify
\r
9 * it under the terms of the GNU Lesser General Public License as published
\r
10 * by the Free Software Foundation, either version 3 of the License, or
\r
11 * (at your option) any later version.
\r
13 * This program is distributed in the hope that it will be useful,
\r
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
16 * GNU General Public License for more details.
\r
18 * You should have received a copy of the GNU Lesser General Public License
\r
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
\r
21 * For further information please contact.
\r
23 * <airmail(at)ebony.plala.or.jp> or <nyatla(at)nyatla.jp>
\r
25 *********************************************************************************/
\r
26 #include "NyLPC_cSsdpSocket.h"
\r
27 #include "NyLPC_http.h"
\r
28 #include "NyLPC_netif.h"
\r
38 #define HTTP_SP 0x20
\r
40 #define PARSE_NULL 0
\r
41 #define PARSE_ST 0x01
\r
42 #define PARSE_MAN 0x11
\r
43 #define PARSE_UNKNOWN 0xff
\r
45 static const struct NyLPC_TIPv4Addr SSDP_MCAST_IPADDR=NyLPC_TIPv4Addr_pack(239,255,255,250);
\r
46 static const char* STR_UPNP_ROOT_DEVICE="upnp:rootdevice";
\r
48 struct TMSearchHeader
\r
50 struct NyLPC_THttpBasicHeader super;
\r
52 const struct NyLPC_TUPnPDeviceRecord* _ref_devices;
\r
60 const NyLPC_TChar* _rpos;
\r
62 const NyLPC_TChar* st_str;
\r
63 const NyLPC_TChar* man_str;
\r
64 NyLPC_TUInt16 st_len;
\r
65 NyLPC_TUInt16 man_len;
\r
70 void NyLPC_cSsdpSocket_notify(NyLPC_TcSsdpSocket_t* i_inst);
\r
73 static NyLPC_TBool urlHandler(NyLPC_TcHttpBasicHeaderParser_t* i_inst,NyLPC_TChar i_c,struct NyLPC_THttpBasicHeader* o_out)
\r
76 return NyLPC_TBool_TRUE;
\r
79 #define TIMEOUT_IN_MS 100
\r
82 * SERVER MessageHeaderの値
\r
85 #define SERVER_MESSAGE_HEADER "MiMic/1.4 UPnP/1.0 MiMicUPnP/0.2"
\r
89 * MsearchResponseを格納したTxパケットをAllocする。
\r
97 * MsearchResponseを格納したTXメモリ
\r
99 static void* allocMsearchResponeTx(
\r
100 NyLPC_TcSsdpSocket_t* i_inst,
\r
101 const NyLPC_TChar* i_st,
\r
102 const NyLPC_TChar* i_udn,
\r
103 const NyLPC_TChar* i_usn,
\r
104 NyLPC_TUInt16 i_st_len,
\r
105 NyLPC_TInt16* o_len)
\r
109 NyLPC_TUInt16 len_usn=(NyLPC_TUInt16)((i_usn!=NULL)?strlen(i_usn):0);
\r
110 NyLPC_TUInt16 len_udn=(NyLPC_TUInt16)strlen(i_udn);
\r
111 NyLPC_TUInt16 len_location=(NyLPC_TUInt16)strlen(i_inst->location_path);
\r
114 // "HTTP/1.1 200 OK\r\n" //15+2=17
\r
115 // "CACHE-CONTROL: max-age = nnnn\r\n" //29+2=31
\r
116 // "SERVER: [:40byte:]\r\n" //8+40+2=50
\r
117 // "EXT: \r\n" //5+2 = 7
\r
118 // "LOCATION: http://xxx.xxx.xxx.xxx:nnnnn/%s/d.xml\r\n" //34+2=46
\r
119 // "USN: %s%s\r\n" //5+2=7
\r
120 // "ST: %s\r\n\r\n" //4+4=8
\r
121 l=166+len_location+len_usn+len_udn+i_st_len;
\r
122 obuf=NyLPC_iUdpSocket_allocSendBuf(i_inst->_socket,l,&l,TIMEOUT_IN_MS);
\r
128 if(l<161+len_location+len_usn+len_udn+i_st_len)
\r
130 NyLPC_iUdpSocket_releaseSendBuf(i_inst->_socket,obuf);
\r
136 "HTTP/1.1 200 OK\r\n"
\r
137 "CACHE-CONTROL: max-age = 300\r\n"
\r
138 "SERVER: "SERVER_MESSAGE_HEADER"\r\n"
\r
140 "LOCATION: http://");
\r
143 l+=NyLPC_TIPv4Addr_toString(NyLPC_iUdpSocket_getSockIP(i_inst->_socket),obuf+l);
\r
145 l+=1+NyLPC_itoa(i_inst->location_port,obuf+l+1,10);
\r
147 memcpy(obuf+l,i_inst->location_path,len_location);l+=len_location;
\r
148 memcpy(obuf+l,"/d.xml",6);l+=6;
\r
153 memcpy(obuf+l,"USN: ",5); l+=5;
\r
154 memcpy(obuf+l,i_udn,len_udn); l+=len_udn; //uuid:xxx
\r
159 memcpy(obuf+l,i_usn,len_usn);l+=len_usn; //usn:xxx
\r
165 memcpy(obuf+l,"ST: ",4); l+=4;
\r
166 memcpy(obuf+l,i_st,i_st_len);l+=i_st_len;
\r
167 memcpy(obuf+l,"\r\n\r\n",4); l+=4;
\r
174 * MsearchResponseを格納したTxパケットをAllocする。
\r
182 * MsearchResponseを格納したTXメモリ
\r
184 static void* allocNotifyTx(
\r
185 NyLPC_TcSsdpSocket_t* i_inst,
\r
186 const NyLPC_TChar* i_udn,
\r
187 const NyLPC_TChar* i_usn,
\r
188 NyLPC_TInt16* o_len)
\r
191 NyLPC_TUInt16 l,l2;
\r
192 NyLPC_TUInt16 len_usn=(NyLPC_TUInt16)((i_usn!=NULL)?strlen(i_usn):0);
\r
193 NyLPC_TUInt16 len_udn=(NyLPC_TUInt16)strlen(i_udn);
\r
194 NyLPC_TUInt16 len_location=(NyLPC_TUInt16)strlen(i_inst->location_path);
\r
197 // "NOTIFY * HTTP/1.1\r\n" //15+2=17
\r
198 // "HOST: 239.255.255.250:1900\r\n" //26+2=28
\r
199 // "CACHE-CONTROL: max-age = 1800\r\n" //29+2=31
\r
200 // "SERVER: [:40byte:]\r\n" //8+40+2=50
\r
201 // "NTS: ssdp:alive\r\n" //14+2 =17
\r
202 // "LOCATION: http://xxx.xxx.xxx.xxx:nnnnn/%s/d.xml\r\n"//44+2=46
\r
203 // "USN: %s%s\r\n" //5+2=7
\r
204 // "NT: %s\r\n\r\n" //4+4=8
\r
205 l2=204+len_location+len_usn+len_udn+((len_usn>0)?len_usn:len_udn);
\r
206 obuf=NyLPC_iUdpSocket_allocSendBuf(i_inst->_socket,l2,&l,TIMEOUT_IN_MS);
\r
213 NyLPC_iUdpSocket_releaseSendBuf(i_inst->_socket,obuf);
\r
219 "NOTIFY * HTTP/1.1\r\n"
\r
220 "HOST: 239.255.255.250:1900\r\n"
\r
221 "CACHE-CONTROL: max-age = 300\r\n"
\r
222 "SERVER: "SERVER_MESSAGE_HEADER"\r\n"
\r
223 "NTS: ssdp:alive\r\n"
\r
224 "LOCATION: http://");
\r
227 l+=NyLPC_TIPv4Addr_toString(NyLPC_iUdpSocket_getSockIP(i_inst->_socket),obuf+l);
\r
229 l+=1+NyLPC_itoa(i_inst->location_port,obuf+l+1,10);
\r
231 memcpy(obuf+l,i_inst->location_path,len_location);l+=len_location;
\r
232 memcpy(obuf+l,"/d.xml",6);l+=6;
\r
237 memcpy(obuf+l,"USN: ",5); l+=5;
\r
238 memcpy(obuf+l,i_udn,len_udn); l+=len_udn; //uuid:xxx
\r
243 memcpy(obuf+l,i_usn,len_usn);l+=len_usn; //usn:xxx
\r
249 memcpy(obuf+l,"NT: ",4); l+=4;
\r
251 memcpy(obuf+l,i_usn,len_usn);l+=len_usn;
\r
253 memcpy(obuf+l,i_udn,len_udn);l+=len_udn;
\r
255 memcpy(obuf+l,"\r\n\r\n",4); l+=4;
\r
261 static NyLPC_TBool messageHandler(NyLPC_TcHttpBasicHeaderParser_t* i_inst,const NyLPC_TChar* i_name,NyLPC_TChar i_c,struct NyLPC_THttpBasicHeader* o_out)
\r
263 struct TMSearchHeader* header=(struct TMSearchHeader*)o_out;
\r
267 if(NyLPC_stricmp(i_name,"ST")==0){
\r
269 header->st=PARSE_ST;
\r
270 header->result.st_str=NULL;
\r
271 }else if(NyLPC_stricmp(i_name,"MAN")==0){
\r
273 header->st=PARSE_MAN;
\r
274 header->result.man_str=NULL;
\r
276 header->st=PARSE_UNKNOWN;
\r
281 if((header->result.st_str==NULL) && (i_c!=HTTP_SP)){
\r
282 header->result.st_str=header->_rpos;
\r
286 header->result.st_len=header->_rpos-header->result.st_str-1;
\r
287 header->st=PARSE_NULL;
\r
291 if((header->result.man_str==NULL) && (i_c!=HTTP_SP)){
\r
292 header->result.man_str=header->_rpos;
\r
295 header->result.man_len=header->_rpos-header->result.man_str-1;
\r
296 header->st=PARSE_NULL;
\r
299 case PARSE_UNKNOWN:
\r
302 header->st=PARSE_NULL;
\r
306 return NyLPC_TBool_TRUE;
\r
312 static const struct NyLPC_TcHttpBasicHeaderParser_Handler handler=
\r
318 static NyLPC_TBool parseHeader(struct TMSearchHeader* i_out,const void* i_rx,NyLPC_TInt16 i_rx_size)
\r
321 NyLPC_TcHttpBasicHeaderParser_t parser;
\r
323 i_out->st=PARSE_NULL;
\r
324 i_out->result.st_str=NULL;
\r
325 i_out->result.man_str=NULL;
\r
326 NyLPC_cHttpBasicHeaderParser_initialize(&parser,&handler);
\r
327 NyLPC_cHttpBasicHeaderParser_parseInit(&parser,&(i_out->super));
\r
328 for(i=0;i<i_rx_size;i++){
\r
329 i_out->_rpos=((const char*)(i_rx))+i;
\r
330 if(NyLPC_cHttpBasicHeaderParser_parseChar(&parser,i_out->_rpos,1,&(i_out->super))<0){
\r
331 NyLPC_cHttpBasicHeaderParser_finalize(&parser);
\r
332 return NyLPC_TBool_FALSE;//ERROR
\r
335 NyLPC_cHttpBasicHeaderParser_parseFinish(&parser,&(i_out->super));
\r
336 NyLPC_cHttpBasicHeaderParser_finalize(&parser);
\r
337 return NyLPC_TBool_TRUE;//OK
\r
340 static NyLPC_TBool onPacket(NyLPC_TiUdpSocket_t* i_sock,const void* i_buf,const struct NyLPC_TIPv4RxInfo* i_info)
\r
344 struct TMSearchHeader header;
\r
345 NyLPC_TInt16 tx_len;
\r
347 NyLPC_TcSsdpSocket_t* inst=((NyLPC_TcSsdpSocket_t*)i_sock->_tag);
\r
348 if(!parseHeader(&header,i_buf,i_info->size)){
\r
349 NyLPC_OnErrorGoto(ERROR1);
\r
352 if(header.result.man_str==NULL || header.result.st_str==NULL){
\r
353 NyLPC_OnErrorGoto(ERROR1);
\r
356 if(header.super.startline.req.method!=NyLPC_THttpMethodType_M_SEARCH){
\r
357 NyLPC_OnErrorGoto(ERROR1);
\r
361 if(strncmp("\"ssdp:discover\"",header.result.man_str,15)!=0){
\r
362 NyLPC_OnErrorGoto(ERROR1);
\r
365 if(strncmp("ssdp:all",header.result.st_str,8)==0){
\r
366 tx=allocMsearchResponeTx(
\r
367 inst,header.result.st_str,
\r
368 inst->ref_device_record[0]->udn,STR_UPNP_ROOT_DEVICE,
\r
369 header.result.st_len,
\r
372 NyLPC_OnErrorGoto(ERROR1);
\r
374 if(!NyLPC_iUdpSocket_psend(i_sock,&i_info->peer_ip,i_info->peer_port,tx,tx_len)){
\r
375 NyLPC_OnErrorGoto(ERROR2);
\r
378 for(i=0;i<inst->number_of_device;i++){
\r
379 tx=allocMsearchResponeTx(
\r
380 inst,header.result.st_str,
\r
381 inst->ref_device_record[i]->udn,inst->ref_device_record[i]->device_type,
\r
382 header.result.st_len,
\r
385 NyLPC_OnErrorGoto(ERROR1);
\r
387 if(!NyLPC_iUdpSocket_psend(i_sock,&i_info->peer_ip,i_info->peer_port,tx,tx_len)){
\r
388 NyLPC_OnErrorGoto(ERROR2);
\r
390 for(i2=0;i2<inst->ref_device_record[i]->number_of_service;i2++){
\r
392 tx=allocMsearchResponeTx(
\r
393 inst,header.result.st_str,
\r
394 inst->ref_device_record[i]->udn,inst->ref_device_record[i]->services[i2].service_type,
\r
395 header.result.st_len,
\r
398 NyLPC_OnErrorGoto(ERROR1);
\r
400 if(!NyLPC_iUdpSocket_psend(i_sock,&i_info->peer_ip,i_info->peer_port,tx,tx_len)){
\r
401 NyLPC_OnErrorGoto(ERROR2);
\r
405 }else if(strncmp("uuid:",header.result.st_str,5)==0){
\r
408 for(i=inst->number_of_device-1;i>=0;i--){
\r
409 if(strncmp(header.result.st_str,inst->ref_device_record[i]->udn,header.result.st_len)==0){
\r
411 tx=allocMsearchResponeTx(
\r
412 inst,header.result.st_str,
\r
413 inst->ref_device_record[i]->udn,NULL,
\r
414 header.result.st_len,
\r
417 NyLPC_OnErrorGoto(ERROR1);
\r
419 if(!NyLPC_iUdpSocket_psend(i_sock,&i_info->peer_ip,i_info->peer_port,tx,tx_len)){
\r
420 NyLPC_OnErrorGoto(ERROR2);
\r
425 }else if(strncmp(STR_UPNP_ROOT_DEVICE,header.result.st_str,15)==0){
\r
426 //rootDeviceはSTR_UPNP_ROOT_DEVICE
\r
427 tx=allocMsearchResponeTx(
\r
428 inst,header.result.st_str,
\r
429 inst->ref_device_record[0]->udn,STR_UPNP_ROOT_DEVICE,
\r
430 header.result.st_len,
\r
433 NyLPC_OnErrorGoto(ERROR1);
\r
435 if(!NyLPC_iUdpSocket_psend(i_sock,&i_info->peer_ip,i_info->peer_port,tx,tx_len)){
\r
436 NyLPC_OnErrorGoto(ERROR2);
\r
438 }else if(strncmp("urn:",header.result.st_str,4)==0){
\r
439 for(i=0;i<inst->number_of_device;i++){
\r
441 if(strncmp(inst->ref_device_record[i]->device_type,header.result.st_str,header.result.st_len)==0){
\r
443 tx=allocMsearchResponeTx(
\r
444 inst,header.result.st_str,
\r
445 inst->ref_device_record[i]->udn,inst->ref_device_record[i]->device_type,
\r
446 header.result.st_len,
\r
449 NyLPC_OnErrorGoto(ERROR1);
\r
451 if(!NyLPC_iUdpSocket_psend(i_sock,&i_info->peer_ip,i_info->peer_port,tx,tx_len)){
\r
452 NyLPC_OnErrorGoto(ERROR2);
\r
456 for(i2=0;i2<inst->ref_device_record[i]->number_of_service;i2++){
\r
457 if(strncmp(inst->ref_device_record[i]->services[i2].service_type,header.result.st_str,header.result.st_len)==0){
\r
459 tx=allocMsearchResponeTx(
\r
460 inst,header.result.st_str,
\r
461 inst->ref_device_record[i]->udn,inst->ref_device_record[i]->services[i2].service_type,
\r
462 header.result.st_len,
\r
465 NyLPC_OnErrorGoto(ERROR1);
\r
467 if(!NyLPC_iUdpSocket_psend(i_sock,&i_info->peer_ip,i_info->peer_port,tx,tx_len)){
\r
468 NyLPC_OnErrorGoto(ERROR2);
\r
475 return NyLPC_TBool_FALSE;
\r
477 NyLPC_iUdpSocket_releaseSendBuf(i_sock,tx);
\r
479 return NyLPC_TBool_FALSE;
\r
482 #define SSDP_NOTIFY_INTERVAL 150*1000 //300*0.5*1000
\r
483 #define FLAG_ORDER_START_SERVICE 0
\r
484 #define FLAG_ORDER_STOP_SERVICE 1
\r
485 #define FLAG_IS_SERVICE_RUNNING 2
\r
487 static void onPeriodic(NyLPC_TiUdpSocket_t* i_sock)
\r
489 NyLPC_TcSsdpSocket_t* inst=(NyLPC_TcSsdpSocket_t*)i_sock->_tag;
\r
490 if(NyLPC_TUInt8_isBitOn(inst->_flags,FLAG_IS_SERVICE_RUNNING)){
\r
493 if(NyLPC_TUInt8_isBitOn(inst->_flags,FLAG_ORDER_STOP_SERVICE))
\r
496 NyLPC_TUInt8_unsetBit(inst->_flags,FLAG_IS_SERVICE_RUNNING);
\r
498 NyLPC_TUInt8_unsetBit(inst->_flags,FLAG_ORDER_STOP_SERVICE);
\r
500 }else if(NyLPC_cStopwatch_isExpired(&inst->_periodic_sw)){
\r
502 NyLPC_cSsdpSocket_notify(inst);
\r
504 NyLPC_cStopwatch_startExpire(&inst->_periodic_sw,SSDP_NOTIFY_INTERVAL);
\r
509 if(NyLPC_TUInt8_isBitOn(inst->_flags,FLAG_ORDER_START_SERVICE))
\r
512 NyLPC_TUInt8_setBit(inst->_flags,FLAG_IS_SERVICE_RUNNING);
\r
514 NyLPC_TUInt8_unsetBit(inst->_flags,FLAG_ORDER_START_SERVICE);
\r
516 NyLPC_cStopwatch_startExpire(&inst->_periodic_sw,SSDP_NOTIFY_INTERVAL);
\r
524 static void expandDeviceTree(NyLPC_TcSsdpSocket_t* i_inst,const struct NyLPC_TUPnPDevDescDevice* i_dev)
\r
527 if(i_inst->number_of_device>=NyLPC_TcSsdpSocket_MAX_DEVICES){
\r
530 i_inst->ref_device_record[i_inst->number_of_device]=i_dev;
\r
531 i_inst->number_of_device++;
\r
532 for(i=0;i<i_dev->number_of_devices;i++){
\r
533 expandDeviceTree(i_inst,i_dev->devices[i]);
\r
538 void NyLPC_cSsdpSocket_initialize(
\r
539 NyLPC_TcSsdpSocket_t* i_inst,
\r
540 const struct NyLPC_TUPnPDevDescDevice* i_ref_dev_record,
\r
541 NyLPC_TUInt16 i_server_port,const NyLPC_TChar* i_ref_location_path)
\r
543 i_inst->_socket=NyLPC_cNet_createUdpSocketEx(1900,NyLPC_TSocketType_UDP_NOBUF);
\r
544 i_inst->_socket->_tag=i_inst;
\r
546 NyLPC_iUdpSocket_setOnRxHandler(i_inst->_socket,onPacket);
\r
547 NyLPC_iUdpSocket_setOnPeriodicHandler(i_inst->_socket,onPeriodic);
\r
549 NyLPC_iUdpSocket_joinMulticast(i_inst->_socket,&SSDP_MCAST_IPADDR);
\r
551 NyLPC_cStopwatch_initialize(&(i_inst->_periodic_sw));
\r
552 i_inst->number_of_device=0;
\r
553 expandDeviceTree(i_inst,i_ref_dev_record);
\r
554 i_inst->location_port=i_server_port;
\r
555 i_inst->location_path=i_ref_location_path;
\r
557 void NyLPC_cSsdpSocket_finalize(NyLPC_TcSsdpSocket_t* i_inst)
\r
559 NyLPC_cStopwatch_finalize(&(i_inst->_periodic_sw));
\r
560 NyLPC_iUdpSocket_finalize(i_inst->_socket);
\r
563 void NyLPC_cSsdpSocket_start(NyLPC_TcSsdpSocket_t* i_inst)
\r
567 NyLPC_cSsdpSocket_notify(i_inst);
\r
569 NyLPC_cThread_sleep(800);
\r
570 NyLPC_cSsdpSocket_notify(i_inst);
\r
574 NyLPC_TUInt8_setBit(i_inst->_flags,FLAG_ORDER_START_SERVICE);
\r
576 NyLPC_cThread_sleep(10);
\r
577 //開始フラグがクリアされるまでループ
\r
578 }while(NyLPC_TUInt8_isBitOn(i_inst->_flags,FLAG_ORDER_START_SERVICE));
\r
580 void NyLPC_cSsdpSocket_stop(NyLPC_TcSsdpSocket_t* i_inst)
\r
584 NyLPC_TUInt8_setBit(i_inst->_flags,FLAG_ORDER_STOP_SERVICE);
\r
586 NyLPC_cThread_sleep(10);
\r
587 //開始フラグがクリアされるまでループ
\r
588 }while(NyLPC_TUInt8_isBitOn(i_inst->_flags,FLAG_ORDER_STOP_SERVICE));
\r
590 void NyLPC_cSsdpSocket_notify(NyLPC_TcSsdpSocket_t* i_inst)
\r
593 NyLPC_TInt16 tx_len;
\r
598 i_inst->ref_device_record[0]->udn,STR_UPNP_ROOT_DEVICE,
\r
601 NyLPC_OnErrorGoto(ERROR1);
\r
603 if(!NyLPC_iUdpSocket_psend(i_inst->_socket,&SSDP_MCAST_IPADDR,1900,tx,tx_len)){
\r
604 NyLPC_OnErrorGoto(ERROR2);
\r
607 for(i=0;i<i_inst->number_of_device;i++){
\r
611 i_inst->ref_device_record[i]->udn,NULL,
\r
614 NyLPC_OnErrorGoto(ERROR1);
\r
616 if(!NyLPC_iUdpSocket_psend(i_inst->_socket,&SSDP_MCAST_IPADDR,1900,tx,tx_len)){
\r
617 NyLPC_OnErrorGoto(ERROR2);
\r
622 i_inst->ref_device_record[i]->udn,i_inst->ref_device_record[i]->device_type,
\r
625 NyLPC_OnErrorGoto(ERROR1);
\r
627 if(!NyLPC_iUdpSocket_psend(i_inst->_socket,&SSDP_MCAST_IPADDR,1900,tx,tx_len)){
\r
628 NyLPC_OnErrorGoto(ERROR2);
\r
630 for(i2=0;i2<i_inst->ref_device_record[i]->number_of_service;i2++){
\r
633 i_inst->ref_device_record[i]->udn,i_inst->ref_device_record[i]->services[i2].service_type,
\r
636 NyLPC_OnErrorGoto(ERROR1);
\r
638 if(!NyLPC_iUdpSocket_psend(i_inst->_socket,&SSDP_MCAST_IPADDR,1900,tx,tx_len)){
\r
639 NyLPC_OnErrorGoto(ERROR2);
\r
645 NyLPC_iUdpSocket_releaseSendBuf(i_inst->_socket,tx);
\r