$ git clone http://tcclient.ion.nu/tc_client.git
commit c6c16b216f5e12d24d50c22b9eaac97e39f56b21
Author: Alicia <...>
Date:   Tue Apr 7 06:49:01 2015 +0200

    Version 0.11

diff --git a/ChangeLog b/ChangeLog
index 121b8cd..93f7f26 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,5 @@
+0.11:
+Rewrote the RTMP code to read from the socket instead of from a buffer, this will prevent messages from being dropped when they are larger than the buffer.
 0.10:
 irchack: stay connected if joining a channel fails and tell the IRC client if a channel password is required.
 irchack: keep listening for connections and accept the next after a session ends.
diff --git a/Makefile b/Makefile
index 90bc106..71d3434 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-VERSION=0.10
+VERSION=0.11
 CFLAGS=-g3 -Wall $(shell curl-config --cflags)
 LIBS=-g3 $(shell curl-config --libs)
 
diff --git a/amfparser.c b/amfparser.c
index 77bf617..eadd1f6 100644
--- a/amfparser.c
+++ b/amfparser.c
@@ -16,7 +16,7 @@
 */
 #include <string.h>
 #include <stdlib.h>
-#include <arpa/inet.h>
+#include <endian.h>
 #include <stdio.h> // For debugging
 #include "amfparser.h"
 
@@ -71,9 +71,10 @@ struct amf* amf_parse(const unsigned char* buf, int len)
       obj=&amf->items[amf->itemcount-1].object;
       // TODO: recurse into unfinished member-objects (unimportant, I haven't seen any objects within objects so far)
       // Add member and set name
-      i=ntohs(*(short*)buf);
+      i=be16toh(*(short*)buf);
       buf=&buf[sizeof(short)];
-      if(buf[i]!=9) // 9=end-of-object
+      if(&buf[i]>=end){printf("Warning: skipping object item with name exceeding RTMP size (0x%x)\n", i);}
+      if(&buf[i]<end && buf[i]!=9) // 9=end-of-object
       {
         ++obj->membercount;
         obj->members=realloc(obj->members, sizeof(struct amfobjectmember)*obj->membercount);
@@ -83,6 +84,7 @@ struct amf* amf_parse(const unsigned char* buf, int len)
         obj->ended=1;
         obj=0;
         buf=&buf[1]; // Skip object-end
+        continue;
       }
     }
     switch(buf[0])
@@ -113,7 +115,7 @@ struct amf* amf_parse(const unsigned char* buf, int len)
         item=&obj->members[obj->membercount-1].value;
       else
         item=amf_newitem(amf);
-      i=ntohs(*(short*)buf);
+      i=be16toh(*(short*)buf);
       buf=&buf[sizeof(short)];
       item->type=AMF_STRING;
       item->string.length=i;
diff --git a/amfwriter.c b/amfwriter.c
index 8dcd543..2011c8f 100644
--- a/amfwriter.c
+++ b/amfwriter.c
@@ -18,10 +18,22 @@
 #include <string.h>
 #include <stdint.h>
 #include <unistd.h>
-#include <arpa/inet.h>
-#include "rtmp.h"
+#include <endian.h>
 #include "amfwriter.h"
 
+#pragma pack(push)
+#pragma pack(1)
+struct rtmph
+{
+  unsigned int streamid:6;
+  unsigned int fmt:2;
+  unsigned int timestamp:24;
+  unsigned int length:24;
+  unsigned char type;
+  unsigned int msgid;
+};
+#pragma pack(pop)
+
 extern unsigned int flip(unsigned int bits, int bytecount);
 
 void amfinit(struct amfmsg* msg)
@@ -91,7 +103,7 @@ void amfstring(struct amfmsg* msg, const char* string)
   unsigned char* type=msg->buf+offset;
   type[0]='\x02';
   uint16_t* strlength=(uint16_t*)(msg->buf+offset+1);
-  *strlength=htons(len);
+  *strlength=htobe16(len);
   memcpy(msg->buf+offset+3, string, len);
 }
 
@@ -111,7 +123,7 @@ void amfobjitem(struct amfmsg* msg, char* name)
   msg->len+=2+len;
   msg->buf=realloc(msg->buf, sizeof(struct rtmph)+msg->len);
   uint16_t* strlength=(uint16_t*)(msg->buf+offset);
-  *strlength=htons(len);
+  *strlength=htobe16(len);
   memcpy(msg->buf+offset+2, name, len);
 }
 
diff --git a/client.c b/client.c
index 5b60035..811965a 100644
--- a/client.c
+++ b/client.c
@@ -199,6 +199,7 @@ int main(int argc, char** argv)
   b_read(sock, handshake, 1536); // Read our junk back, we don't bother checking that it's the same
   printf("Handshake complete\n");
   // Handshake complete, send connect request
+  struct rtmp rtmp={0,0,0};
   struct amfmsg amf;
   amfinit(&amf);
   amfstring(&amf, "connect");
@@ -353,24 +354,11 @@ int main(int argc, char** argv)
     }
     // Got data from server
     pfd[1].revents=0;
-// TODO: This should be done differently, first reading one byte to get the size of the header, then read the rest of the header and thus get the length of the content
-    len=read(sock, buf, 2048);
-//  printf("Received %i byte response\n", len);
-    if(len<=0){printf("Server disconnected\n"); break;}
-/* Debugging
-  char name[128];
-  sprintf(name, "output%i", outnum);
-  f=open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644);
-  write(f, buf, len);
-  close(f);
-*/
-    // Separate and handle all AMF0 buffers in the RTMP stream
-    unsigned char* rtmp_pos=buf;
-    int rtmp_len;
-    unsigned char* amfbuf;
-    while((amfbuf=rtmp_getamf(&rtmp_pos, &len, &rtmp_len)))
+    // Read the RTMP stream and handle AMF0 packets
+    if(rtmp_get(sock, &rtmp))
     {
-      struct amf* amfin=amf_parse(amfbuf, rtmp_len);
+      if(rtmp.type!=RTMP_AMF0){printf("Got RTMP type 0x%x\n", rtmp.type); continue;}
+      struct amf* amfin=amf_parse(rtmp.buf, rtmp.length);
       if(amfin->itemcount>0 && amfin->items[0].type==AMF_STRING)
       {
 //        printf("Got command: '%s'\n", amfin->items[0].string.string);
@@ -494,8 +482,10 @@ int main(int argc, char** argv)
       // else{printf("Unknown command...\n"); printamf(amfin);} // (Debugging)
       amf_free(amfin);
     }
+    else{printf("Server disconnected\n"); break;}
 //    ++outnum; (Debugging)
   }
+  free(rtmp.buf);
   close(sock);
   return 0;
 }
diff --git a/crossbuild.sh b/crossbuild.sh
index 7ff352e..09b17b8 100755
--- a/crossbuild.sh
+++ b/crossbuild.sh
@@ -6,16 +6,16 @@ if [ "$host" = "" ]; then
   echo "No target host specified (argv[1]), defaulting to ${host}"
 fi
 here="`pwd`"
-if false; then
-wget -c http://curl.haxx.se/download/curl-7.39.0.tar.bz2
-tar -xjf curl-7.39.0.tar.bz2
-cd curl-7.39.0
-mkdir -p build
-cd build
-../configure --prefix="${here}/curlprefix" --host="$host" --enable-static --disable-shared --disable-gopher --disable-ftp --disable-tftp --disable-ssh --disable-telnet --disable-dict --disable-file --disable-imap --disable-pop3 --disable-smtp --disable-ldap --without-librtmp --disable-rtsp --without-ssl --disable-sspi --without-nss --without-gnutls --without-libidn
-make
-make install
-cd "$here"
+if [ ! -e curlprefix ]; then
+  wget -c http://curl.haxx.se/download/curl-7.39.0.tar.bz2
+  tar -xjf curl-7.39.0.tar.bz2
+  cd curl-7.39.0
+  mkdir -p build
+  cd build
+  ../configure --prefix="${here}/curlprefix" --host="$host" --enable-static --disable-shared --disable-gopher --disable-ftp --disable-tftp --disable-ssh --disable-telnet --disable-dict --disable-file --disable-imap --disable-pop3 --disable-smtp --disable-ldap --without-librtmp --disable-rtsp --without-ssl --disable-sspi --without-nss --without-gnutls --without-libidn
+  make
+  make install
+  cd "$here"
 fi
 export PATH="${here}/curlprefix/bin:${PATH}"
 # Select a crosscompiler (if we can find one)
diff --git a/rtmp.c b/rtmp.c
index 9926621..bbb4da8 100644
--- a/rtmp.c
+++ b/rtmp.c
@@ -14,68 +14,69 @@
     You should have received a copy of the GNU Affero General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
 #include <string.h>
+#include <endian.h>
 #include "rtmp.h"
 
-extern unsigned int flip(unsigned int bits, int bytecount);
-
-int rtmp_lastlen=0;
-int rtmp_lasttype=0;
-unsigned char* rtmp_getamf(unsigned char** msg, int* length, int* amflen)
+char rtmp_get(int sock, struct rtmp* rtmp)
 {
-  int headsize;
-  struct rtmph* head=(struct rtmph*)*msg;
-  while((void*)head<((void*)*msg)+*length)
+  static unsigned int length;
+  static unsigned char type;
+  static unsigned int timestamp;
+  // Header format and stream ID
+  unsigned int x=0;
+  if(read(sock, &x, 1)<1){return 0;}
+  unsigned int streamid=x&0x3f;
+  unsigned int fmt=(x&0xc0)>>6;
+  // Handle extended stream IDs
+  if(streamid<2) // (0=1 extra byte, 1=2 extra bytes)
   {
-    int len=flip(head->length, 3);
-    int type;
-    switch(head->fmt)
-    {
-      case 0: headsize=12; break;
-      case 1: headsize=8; break;
-      case 2: headsize=4; break;
-      case 3: headsize=1; break;
-    }
-//  printf("fmt: %u\n", (unsigned int)head->fmt);
-//  printf("streamid: %u\n", (unsigned int)head->streamid);
-//  printf("timestamp: %u\n", (unsigned int)head->timestamp);
-    if(headsize>=8){
-//    printf("length: %u\n", flip(head->length, 3));
-//    printf("type: %u\n", (unsigned int)head->type);
-      type=head->type;
-//    if(head->fmt==0){printf("msgid: %u\n", (unsigned int)head->msgid);}
-      rtmp_lasttype=head->type;
-      rtmp_lastlen=len;
-    }else{
-      type=rtmp_lasttype;
-      len=rtmp_lastlen;
-    }
-
-    int skip=headsize+len+(len/128);
-//      printf("Skipping %i bytes to next message (%i + %i + %i)\n\n", skip, headsize, len, len/128);
-    *msg+=skip;
-    *length-=skip;
-    if(type==0x14)
+    read(sock, &x, streamid+1);
+    streamid=64+x;
+  }
+  if(fmt<3)
+  {
+    // Timestamp
+    read(sock, &x, 3);
+    timestamp=x;
+    if(fmt<2)
     {
-      unsigned char* data=((void*)head)+headsize;
-      // Cut out every 128th byte (0xc3 garbage required by RTMP)
-      int i;
-      skip=0;
-      for(i=128; i<len; i+=128)
+      // Length
+      x=0;
+      read(sock, ((void*)&x)+1, 3);
+      length=be32toh(x);
+      // Type
+      read(sock, &type, sizeof(type));
+      if(fmt<1)
       {
-        ++skip;
-// printf("Skipping garbage byte '%x' at offset %i\n", (int)data[i]&0xff, i);
-        if(i+128<len)
-        {
-          memmove(&data[i], &data[i+skip], 128);
-        }else{
-          memmove(&data[i], &data[i+skip], len-i);
-        }
+        // Message ID (is this ever used?)
+        unsigned int msgid;
+        read(sock, &msgid, sizeof(msgid));
       }
-      *amflen=len;
-      return data;
     }
-    head=((void*)head)+skip;
   }
-  return 0;
+  // Extended timestamp
+  if(timestamp==0xffffff)
+  {
+    read(sock, &x, sizeof(x));
+    timestamp=be32toh(x);
+  }
+
+  rtmp->type=type;
+  rtmp->streamid=streamid;
+  rtmp->length=length+length/128;
+  free(rtmp->buf);
+  rtmp->buf=malloc(rtmp->length);
+  read(sock, rtmp->buf, rtmp->length);
+  int i;
+  for(i=128; i<rtmp->length; i+=128)
+  {
+    --rtmp->length;
+    // printf("Dropping garbage byte %x\n", (unsigned int)((unsigned char*)rtmp->buf)[i]&0xff);
+    memmove(rtmp->buf+i, rtmp->buf+i+1, rtmp->length-i);
+  }
+  return 1;
 }
diff --git a/rtmp.h b/rtmp.h
index fb4035d..b426079 100644
--- a/rtmp.h
+++ b/rtmp.h
@@ -14,17 +14,22 @@
     You should have received a copy of the GNU Affero General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
-#pragma pack(push)
-#pragma pack(1)
-struct rtmph
+#define RTMP_SET_PACKET_SIZE 0x01
+#define RTMP_PING            0x04
+#define RTMP_SERVER_BW       0x05
+#define RTMP_CLIENT_BW       0x06
+#define RTMP_AUDIO           0x08
+#define RTMP_VIDEO           0x09
+#define RTMP_AMF3            0x11
+#define RTMP_INVOKE          0x12
+#define RTMP_AMF0            0x14
+
+struct rtmp
 {
-  unsigned int streamid:6;
-  unsigned int fmt:2;
-  unsigned int timestamp:24;
-  unsigned int length:24;
   unsigned char type;
-  unsigned int msgid;
+  unsigned int streamid;
+  unsigned int length;
+  void* buf;
 };
-#pragma pack(pop)
 
-extern unsigned char* rtmp_getamf(unsigned char** msg, int* length, int* amflen);
+extern char rtmp_get(int sock, struct rtmp* rtmp);