/* * Copyright (C) 2000-2001 major mms * * This file is part of libmms * * libmms is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * libmms is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * * utility functions to handle communication with an mms server */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "bswap.h" #include "mms.h" /* #define LOG */ /* * mms specific types */ #define MMS_PORT 1755 #define BUF_SIZE 102400 #define CMD_HEADER_LEN 48 #define CMD_BODY_LEN 1024 char *mms_url_s[]={"MMS://","MMSU://","MMST://","HTTP://", "HREF",NULL}; char *mms_url_e[]={"ASF","WMF","WMV","WMA",NULL}; struct mms_s { int s; char *host; char *path; char *file; char *url; /* command to send */ char scmd[CMD_HEADER_LEN+CMD_BODY_LEN]; char *scmd_body; /* pointer to &scmd[CMD_HEADER_LEN] */ int scmd_len; /* num bytes written in header */ char str[1024]; /* scratch buffer to built strings */ /* receive buffer */ char buf[BUF_SIZE]; int buf_size; int buf_read; uint8_t asf_header[8192]; int asf_header_len; int asf_header_read; int seq_num; int num_stream_ids; int stream_ids[20]; int packet_length; uint32_t file_length; }; /* network/socket utility functions */ static int host_connect_attempt(struct in_addr ia, int port) { int s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); struct sockaddr_in sin; if (s==-1) { printf ("libmms: socket(): %s\n", strerror(errno)); return -1; } sin.sin_family = AF_INET; sin.sin_addr = ia; sin.sin_port = htons(port); if (connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1 && errno != EINPROGRESS) { printf ("libmms: connect(): %s\n", strerror(errno)); close(s); return -1; } return s; } int host_connect(const char *host, int port) { struct hostent *h; int i, s; h=gethostbyname(host); if (h==NULL) { printf ("libmms: unable to resolve '%s'.\n", host); return -1; } for (i=0; h->h_addr_list[i]; i++) { struct in_addr ia; memcpy (&ia, h->h_addr_list[i],4); s = host_connect_attempt(ia, port); if(s != -1) return s; } printf ("libmms: unable to connect to '%s'.\n", host); return -1; } static void put_32 (mms_t *this, uint32_t value) { this->scmd[this->scmd_len ] = value % 256; value = value >> 8; this->scmd[this->scmd_len+1] = value % 256 ; value = value >> 8; this->scmd[this->scmd_len+2] = value % 256 ; value = value >> 8; this->scmd[this->scmd_len+3] = value % 256 ; this->scmd_len += 4; } static int send_data (int s, char *buf, int len) { int total; total=0; while (total 0) total += n; else if (n<0 && errno!=EAGAIN) return total; } return total; } static uint32_t get_32 (unsigned char *cmd, int offset) { uint32_t ret; ret = cmd[offset] ; ret |= cmd[offset+1]<<8 ; ret |= cmd[offset+2]<<16 ; ret |= cmd[offset+3]<<24 ; return ret; } static void send_command (mms_t *this, int command, uint32_t switches, uint32_t extra, int length) { int len8; len8 = (length + (length%8)) / 8; this->scmd_len = 0; put_32 (this, 0x00000001); /* start sequence */ put_32 (this, 0xB00BFACE); /* #-)) */ put_32 (this, length + 32); put_32 (this, 0x20534d4d); /* protocol type "MMS " */ put_32 (this, len8 + 4); put_32 (this, this->seq_num); this->seq_num++; put_32 (this, 0x0); /* unknown */ put_32 (this, 0x0); put_32 (this, len8+2); put_32 (this, 0x00030000 | command); /* dir | command */ put_32 (this, switches); put_32 (this, extra); /* memcpy (&cmd->buf[48], data, length); */ if (send_data (this->s, this->scmd, length+48) != (length+48)) { printf ("libmms: send error\n"); } #ifdef LOG { int i; printf ("\nlibmms: ***************************************************\ncommand sent, %d bytes\n", length+48); printf ("start sequence %08x\n", get_32 (this->scmd, 0)); printf ("command id %08x\n", get_32 (this->scmd, 4)); printf ("length %8x \n", get_32 (this->scmd, 8)); printf ("len8 %8x \n", get_32 (this->scmd, 16)); printf ("sequence # %08x\n", get_32 (this->scmd, 20)); printf ("len8 (II) %8x \n", get_32 (this->scmd, 32)); printf ("dir | comm %08x\n", get_32 (this->scmd, 36)); printf ("switches %08x\n", get_32 (this->scmd, 40)); printf ("ascii contents>"); for (i=48; i<(length+48); i+=2) { unsigned char c = this->scmd[i]; if ((c>=32) && (c<=128)) printf ("%c", c); else printf ("."); } printf ("\n"); printf ("libmms: complete hexdump of package follows:\n"); for (i=0; i<(length+48); i++) { unsigned char c = this->scmd[i]; printf ("%02x", c); if ((i % 16) == 15) printf ("\nlibmms: "); if ((i % 2) == 1) printf (" "); } printf ("\n"); } #endif } static void string_utf16(char *dest, char *src, int len) { int i; memset (dest, 0, 1000); for (i=0; i=32) && (c<128)) printf ("%c", c); else printf (" %02x ", c); } printf ("\n"); #endif } static void get_answer (mms_t *this) { int command = 0x1b; while (command == 0x1b) { int len; len = read (this->s, this->buf, BUF_SIZE) ; if (!len) { printf ("\nalert! eof\n"); return; } print_answer (this->buf, len); command = get_32 (this->buf, 36) & 0xFFFF; if (command == 0x1b) send_command (this, 0x1b, 0, 0, 0); } } static int receive (int s, char *buf, size_t count) { ssize_t len, total; total = 0; while (total < count) { len = read (s, &buf[total], count-total); if (len<0) { perror ("read error:"); return 0; } total += len; #ifdef LOG if (len != 0) { printf ("[%d/%d]", total, count); fflush (stdout); } #endif } return 1; } static void get_header (mms_t *this) { unsigned char pre_header[8]; this->asf_header_len = 0; while (1) { if (!receive (this->s, pre_header, 8)) { printf ("libmms: pre-header read failed\n"); return ; } #ifdef LOG { int i; for (i=0; i<8; i++) printf ("libmms: pre_header[%d] = %02x (%d)\n", i, pre_header[i], pre_header[i]); } #endif if (pre_header[4] == 0x02) { int packet_len; packet_len = (pre_header[7] << 8 | pre_header[6]) - 8; #ifdef LOG printf ("libmms: asf header packet detected, len=%d\n", packet_len); #endif if (!receive (this->s, &this->asf_header[this->asf_header_len], packet_len)) { printf ("libmms: header data read failed\n"); return; } this->asf_header_len += packet_len; if ( (this->asf_header[this->asf_header_len-1] == 1) && (this->asf_header[this->asf_header_len-2]==1)) { printf ("libmms: get header packet finished\n"); return; } } else { int packet_len, command; if (!receive (this->s, (char *) &packet_len, 4)) { printf ("packet_len read failed\n"); return; } packet_len = get_32 ((char *)&packet_len, 0) + 4; #ifdef LOG printf ("command packet detected, len=%d\n", packet_len); #endif if (!receive (this->s, this->buf, packet_len)) { printf ("command data read failed\n"); return ; } command = get_32 (this->buf, 24) & 0xFFFF; #ifdef LOG printf ("command: %02x\n", command); #endif if (command == 0x1b) send_command (this, 0x1b, 0, 0, 0); } printf ("mms: get header packet succ\n"); } } static void interp_header (mms_t *this) { int i; this->packet_length = 0; /* * parse header */ i = 30; while (iasf_header_len) { uint64_t guid_1, guid_2, length; guid_2 = (uint64_t)this->asf_header[i] | ((uint64_t)this->asf_header[i+1]<<8) | ((uint64_t)this->asf_header[i+2]<<16) | ((uint64_t)this->asf_header[i+3]<<24) | ((uint64_t)this->asf_header[i+4]<<32) | ((uint64_t)this->asf_header[i+5]<<40) | ((uint64_t)this->asf_header[i+6]<<48) | ((uint64_t)this->asf_header[i+7]<<56); i += 8; guid_1 = (uint64_t)this->asf_header[i] | ((uint64_t)this->asf_header[i+1]<<8) | ((uint64_t)this->asf_header[i+2]<<16) | ((uint64_t)this->asf_header[i+3]<<24) | ((uint64_t)this->asf_header[i+4]<<32) | ((uint64_t)this->asf_header[i+5]<<40) | ((uint64_t)this->asf_header[i+6]<<48) | ((uint64_t)this->asf_header[i+7]<<56); i += 8; #ifdef LOG printf ("guid found: %016llx%016llx\n", guid_1, guid_2); #endif length = (uint64_t)this->asf_header[i] | ((uint64_t)this->asf_header[i+1]<<8) | ((uint64_t)this->asf_header[i+2]<<16) | ((uint64_t)this->asf_header[i+3]<<24) | ((uint64_t)this->asf_header[i+4]<<32) | ((uint64_t)this->asf_header[i+5]<<40) | ((uint64_t)this->asf_header[i+6]<<48) | ((uint64_t)this->asf_header[i+7]<<56); i += 8; if ( (guid_1 == 0x6cce6200aa00d9a6) && (guid_2 == 0x11cf668e75b22630) ) { printf ("header object\n"); } else if ((guid_1 == 0x6cce6200aa00d9a6) && (guid_2 == 0x11cf668e75b22636)) { printf ("data object\n"); } else if ((guid_1 == 0x6553200cc000e48e) && (guid_2 == 0x11cfa9478cabdca1)) { this->packet_length = get_32(this->asf_header, i+92-24); this->file_length = get_32(this->asf_header, i+40-24); #ifdef LOG printf ("file object, packet length = %d (%d)\n", this->packet_length, get_32(this->asf_header, i+96-24)); #endif } else if ((guid_1 == 0x6553200cc000e68e) && (guid_2 == 0x11cfa9b7b7dc0791)) { int stream_id = this->asf_header[i+48] | this->asf_header[i+49] << 8; #ifdef LOG printf ("stream object, stream id: %d\n", stream_id); #endif this->stream_ids[this->num_stream_ids] = stream_id; this->num_stream_ids++; /* } else if ((guid_1 == 0x) && (guid_2 == 0x)) { printf ("??? object\n"); */ } else { printf ("unknown object\n"); } #ifdef LOG printf ("length : %lld\n", length); #endif i += length-24; } } int mms_url_is(char* url, char** mms_url) { int i=0; char* uptr; printf("mms_url_is l=%d \n",strlen(mms_url[0])); if(!url ) return 0; uptr=strdup(url); uptr=strupr(uptr); while(mms_url[i]){ if(!strncasecmp(uptr,mms_url[i],strlen(mms_url[i]) )){ free(uptr); return strlen(mms_url[i]); } i++; } free(uptr); return 0; } int mms_start_where(char* url) { int i=0; int delta; char *p; char* uptr; if (!url) return -1; uptr=strdup(url); uptr=strupr(uptr); while(mms_url_s[i]){ if((p=strstr(uptr,mms_url_s[i]))){ delta=p-uptr; free(uptr); return delta; } i++; } free(uptr); return -1; } char* mms_connect_common(int *s, int port, char *url, char **host, char** hostend, char **path, char **file) { int hostlen; int proto_len; char *forport; if(!(proto_len=mms_url_is(url,mms_url_s))){ printf ("libmms: invalid url >%s< (should be mms:// http:// - style)\n", url); return NULL; } /* extract hostname */ printf ("libmms: extracting host name \n"); *hostend = strchr(&url[proto_len],'/'); if (!(*hostend)) { printf ("libmms: invalid url >%s<, failed to find hostend\n", url); return NULL; } forport=strchr(&url[proto_len],':'); /* May be they put a port here ?*/ if( forport && forport < *hostend ){ /*if(sscanf(forport,"%d",&port)!=1) port=MMS_PORT; */ hostlen = forport - url - proto_len; } else hostlen = *hostend - url - proto_len; *host = malloc (hostlen+1); strncpy (*host, &url[proto_len], hostlen); (*host)[hostlen]=0; printf (" libmms: host =%s len =%d proto_len =%d\n",*host,hostlen,proto_len); hostlen = *hostend - url - proto_len; /* extract path and file */ if(path) *path = url+hostlen+proto_len+1; if(file) *file = strrchr (url, '/'); /* * try to connect */ printf("here port=%d \n",port); *s = host_connect (*host, port); if (*s == -1) { printf ("libmms: failed to connect\n"); free (*host); return NULL; } return url; } mms_t *mms_connect (char *url_) { mms_t *this; int len,i; char *url; char *url1; char *path; char *file; char *host=NULL, *hostend; int s; url = strdup (url_); url1 = mms_connect_common(&s,MMS_PORT,url,&host,&hostend,&path,&file); if(!url1){ free(url); return NULL; } this = (mms_t*) malloc (sizeof (mms_t)); this->url = url; this->host = host; this->path = path; this->file = file; this->s = s; this->seq_num = 0; this->scmd_body = &this->scmd[CMD_HEADER_LEN]; this->asf_header_len = 0; this->asf_header_read = 0; this->num_stream_ids = 0; this->packet_length = 0; this->buf_size = 0; this->buf_read = 0; printf ("%s %s %s %s ",url,host,path,file); /* * let the negotiations begin... */ /* cmd1 */ sprintf (this->str, "\034\003NSPlayer/7.0.0.1956; {33715801-BAB3-9D85-24E9-03B90328270A}; Host: %s", this->host); string_utf16 (this->scmd_body, this->str, strlen(this->str)+2); send_command (this, 1, 0, 0x0004000b, strlen(this->str) * 2+8); printf("before read \n"); len = read (this->s, this->buf, BUF_SIZE) ; if (len>0) print_answer (this->buf, len); else { printf ("libmms: read failed: %s\n", strerror(errno)); } /* cmd2 */ string_utf16 (&this->scmd_body[8], "\002\000\\\\192.168.0.129\\TCP\\1037\0000", 28); memset (this->scmd_body, 0, 8); send_command (this, 2, 0, 0, 28*2+8); len = read (this->s, this->buf, BUF_SIZE) ; if (len) print_answer (this->buf, len); /* 0x5 */ string_utf16 (&this->scmd_body[8], path, strlen(path)); memset (this->scmd_body, 0, 8); send_command (this, 5, 0, 0, strlen(path)*2+12); get_answer (this); /* 0x15 */ memset (this->scmd_body, 0, 40); this->scmd_body[32] = 2; send_command (this, 0x15, 1, 0, 40); this->num_stream_ids = 0; get_header (this); interp_header (this); /* 0x33 */ memset (this->scmd_body, 0, 40); for (i=1; inum_stream_ids; i++) { this->scmd_body [ (i-1) * 6 + 2 ] = 0xFF; this->scmd_body [ (i-1) * 6 + 3 ] = 0xFF; this->scmd_body [ (i-1) * 6 + 4 ] = this->stream_ids[i]; this->scmd_body [ (i-1) * 6 + 5 ] = 0x00; } send_command (this, 0x33, this->num_stream_ids, 0xFFFF | this->stream_ids[0] << 16, (this->num_stream_ids-1)*6+2); get_answer (this); /* 0x07 */ memset (this->scmd_body, 0, 40); for (i=8; i<16; i++) this->scmd_body[i] = 0xFF; this->scmd_body[20] = 0x04; send_command (this, 0x07, 1, 0xFFFF | this->stream_ids[0] << 16, 24); printf(" mms_connect: passed\n" ); return this; } static int get_media_packet (mms_t *this) { unsigned char pre_header[8]; if (!receive (this->s, pre_header, 8)) { printf ("pre-header read failed\n"); return 0; } #ifdef LOG for (i=0; i<8; i++) printf ("pre_header[%d] = %02x (%d)\n", i, pre_header[i], pre_header[i]); #endif if (pre_header[4] == 0x04) { int packet_len; packet_len = (pre_header[7] << 8 | pre_header[6]) - 8; #ifdef LOG printf ("asf media packet detected, len=%d\n", packet_len); #endif if (!receive (this->s, this->buf, packet_len)) { printf ("media data read failed\n"); return 0; } /* implicit padding (with "random" data)*/ this->buf_size = this->packet_length; } else { int packet_len, command; if (!receive (this->s, (char *)&packet_len, 4)) { printf ("packet_len read failed\n"); return 0; } packet_len = get_32 ((char *)&packet_len, 0) + 4; #ifdef LOG printf ("command packet detected, len=%d\n", packet_len); #endif if (!receive (this->s, this->buf, packet_len)) { printf ("command data read failed\n"); return 0; } if ( (pre_header[7] != 0xb0) || (pre_header[6] != 0x0b) || (pre_header[5] != 0xfa) || (pre_header[4] != 0xce) ) { printf ("missing signature\n"); exit (1); } command = get_32 (this->buf, 24) & 0xFFFF; #ifdef LOG printf ("command: %02x\n", command); #endif if (command == 0x1b) send_command (this, 0x1b, 0, 0, 0); else if (command == 0x1e) { printf ("libmms: everything done. Thank you for downloading a media file containing proprietary and patentend technology.\n"); return 0; } else if (command != 0x05) { printf ("unknown command %02x\n", command); exit (1); } } #ifdef LOG printf ("get media packet succ\n"); #endif return 1; } int mms_peek_header (mms_t *this, char *data) { memcpy (data, this->asf_header, this->asf_header_len); return this->asf_header_len; } int mms_read (mms_t *this, char *data, int len) { int total; total = 0; while (total < len) { #ifdef LOG printf ("libmms: read, got %d / %d bytes\n", total, len); #endif if (this->asf_header_read < this->asf_header_len) { int n, bytes_left ; bytes_left = this->asf_header_len - this->asf_header_read ; if (lenasf_header[this->asf_header_read], n); this->asf_header_read += n; total += n; } else { int n, bytes_left ; bytes_left = this->buf_size - this->buf_read; while (!bytes_left) { this->buf_read = 0; if (!get_media_packet (this)) { printf ("libmms: get_media_packet failed\n"); return total; } bytes_left = this->buf_size - this->buf_read; } if (lenbuf[this->buf_read], n); this->buf_read += n; total += n; } } return total; } void mms_close (mms_t *this) { if (this->s >= 0) { close(this->s); } free (this->host); free (this->url); free (this); } uint32_t mms_get_length (mms_t *this) { return this->file_length; }