├── librtmp.xcodeproj └── project.pbxproj └── librtmp ├── amf.c ├── amf.h ├── bytes.h ├── dh.h ├── dhgroups.h ├── handshake.h ├── hashswf.c ├── http.h ├── librtmp-Prefix.pch ├── log.c ├── log.h ├── parseurl.c ├── rtmp.c ├── rtmp.h └── rtmp_sys.h /librtmp.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 76FACC9D145BB33D00B3F6C3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76FACC9C145BB33D00B3F6C3 /* Foundation.framework */; }; 11 | 76FACCB7145BB3C200B3F6C3 /* amf.c in Sources */ = {isa = PBXBuildFile; fileRef = 76FACCA9145BB3C200B3F6C3 /* amf.c */; }; 12 | 76FACCB8145BB3C200B3F6C3 /* amf.h in Headers */ = {isa = PBXBuildFile; fileRef = 76FACCAA145BB3C200B3F6C3 /* amf.h */; }; 13 | 76FACCB9145BB3C200B3F6C3 /* bytes.h in Headers */ = {isa = PBXBuildFile; fileRef = 76FACCAB145BB3C200B3F6C3 /* bytes.h */; }; 14 | 76FACCBA145BB3C200B3F6C3 /* dh.h in Headers */ = {isa = PBXBuildFile; fileRef = 76FACCAC145BB3C200B3F6C3 /* dh.h */; }; 15 | 76FACCBB145BB3C200B3F6C3 /* dhgroups.h in Headers */ = {isa = PBXBuildFile; fileRef = 76FACCAD145BB3C200B3F6C3 /* dhgroups.h */; }; 16 | 76FACCBC145BB3C200B3F6C3 /* handshake.h in Headers */ = {isa = PBXBuildFile; fileRef = 76FACCAE145BB3C200B3F6C3 /* handshake.h */; }; 17 | 76FACCBD145BB3C200B3F6C3 /* hashswf.c in Sources */ = {isa = PBXBuildFile; fileRef = 76FACCAF145BB3C200B3F6C3 /* hashswf.c */; }; 18 | 76FACCBE145BB3C200B3F6C3 /* http.h in Headers */ = {isa = PBXBuildFile; fileRef = 76FACCB0145BB3C200B3F6C3 /* http.h */; }; 19 | 76FACCBF145BB3C200B3F6C3 /* log.c in Sources */ = {isa = PBXBuildFile; fileRef = 76FACCB1145BB3C200B3F6C3 /* log.c */; }; 20 | 76FACCC0145BB3C200B3F6C3 /* log.h in Headers */ = {isa = PBXBuildFile; fileRef = 76FACCB2145BB3C200B3F6C3 /* log.h */; }; 21 | 76FACCC1145BB3C200B3F6C3 /* parseurl.c in Sources */ = {isa = PBXBuildFile; fileRef = 76FACCB3145BB3C200B3F6C3 /* parseurl.c */; }; 22 | 76FACCC2145BB3C200B3F6C3 /* rtmp_sys.h in Headers */ = {isa = PBXBuildFile; fileRef = 76FACCB4145BB3C200B3F6C3 /* rtmp_sys.h */; }; 23 | 76FACCC3145BB3C200B3F6C3 /* rtmp.c in Sources */ = {isa = PBXBuildFile; fileRef = 76FACCB5145BB3C200B3F6C3 /* rtmp.c */; }; 24 | 76FACCC4145BB3C200B3F6C3 /* rtmp.h in Headers */ = {isa = PBXBuildFile; fileRef = 76FACCB6145BB3C200B3F6C3 /* rtmp.h */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 76FACC99145BB33D00B3F6C3 /* librtmp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = librtmp.a; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 76FACC9C145BB33D00B3F6C3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 30 | 76FACCA0145BB33D00B3F6C3 /* librtmp-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "librtmp-Prefix.pch"; sourceTree = ""; }; 31 | 76FACCA9145BB3C200B3F6C3 /* amf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = amf.c; sourceTree = ""; }; 32 | 76FACCAA145BB3C200B3F6C3 /* amf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = amf.h; sourceTree = ""; }; 33 | 76FACCAB145BB3C200B3F6C3 /* bytes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bytes.h; sourceTree = ""; }; 34 | 76FACCAC145BB3C200B3F6C3 /* dh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dh.h; sourceTree = ""; }; 35 | 76FACCAD145BB3C200B3F6C3 /* dhgroups.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dhgroups.h; sourceTree = ""; }; 36 | 76FACCAE145BB3C200B3F6C3 /* handshake.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = handshake.h; sourceTree = ""; }; 37 | 76FACCAF145BB3C200B3F6C3 /* hashswf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hashswf.c; sourceTree = ""; }; 38 | 76FACCB0145BB3C200B3F6C3 /* http.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = http.h; sourceTree = ""; }; 39 | 76FACCB1145BB3C200B3F6C3 /* log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = log.c; sourceTree = ""; }; 40 | 76FACCB2145BB3C200B3F6C3 /* log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log.h; sourceTree = ""; }; 41 | 76FACCB3145BB3C200B3F6C3 /* parseurl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = parseurl.c; sourceTree = ""; }; 42 | 76FACCB4145BB3C200B3F6C3 /* rtmp_sys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rtmp_sys.h; sourceTree = ""; }; 43 | 76FACCB5145BB3C200B3F6C3 /* rtmp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rtmp.c; sourceTree = ""; }; 44 | 76FACCB6145BB3C200B3F6C3 /* rtmp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rtmp.h; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 76FACC96145BB33D00B3F6C3 /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | 76FACC9D145BB33D00B3F6C3 /* Foundation.framework in Frameworks */, 53 | ); 54 | runOnlyForDeploymentPostprocessing = 0; 55 | }; 56 | /* End PBXFrameworksBuildPhase section */ 57 | 58 | /* Begin PBXGroup section */ 59 | 76FACC8E145BB33D00B3F6C3 = { 60 | isa = PBXGroup; 61 | children = ( 62 | 76FACC9E145BB33D00B3F6C3 /* librtmp */, 63 | 76FACC9B145BB33D00B3F6C3 /* Frameworks */, 64 | 76FACC9A145BB33D00B3F6C3 /* Products */, 65 | ); 66 | sourceTree = ""; 67 | }; 68 | 76FACC9A145BB33D00B3F6C3 /* Products */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 76FACC99145BB33D00B3F6C3 /* librtmp.a */, 72 | ); 73 | name = Products; 74 | sourceTree = ""; 75 | }; 76 | 76FACC9B145BB33D00B3F6C3 /* Frameworks */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 76FACC9C145BB33D00B3F6C3 /* Foundation.framework */, 80 | ); 81 | name = Frameworks; 82 | sourceTree = ""; 83 | }; 84 | 76FACC9E145BB33D00B3F6C3 /* librtmp */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 76FACCA9145BB3C200B3F6C3 /* amf.c */, 88 | 76FACCAA145BB3C200B3F6C3 /* amf.h */, 89 | 76FACCAB145BB3C200B3F6C3 /* bytes.h */, 90 | 76FACCAC145BB3C200B3F6C3 /* dh.h */, 91 | 76FACCAD145BB3C200B3F6C3 /* dhgroups.h */, 92 | 76FACCAE145BB3C200B3F6C3 /* handshake.h */, 93 | 76FACCAF145BB3C200B3F6C3 /* hashswf.c */, 94 | 76FACCB0145BB3C200B3F6C3 /* http.h */, 95 | 76FACCB1145BB3C200B3F6C3 /* log.c */, 96 | 76FACCB2145BB3C200B3F6C3 /* log.h */, 97 | 76FACCB3145BB3C200B3F6C3 /* parseurl.c */, 98 | 76FACCB4145BB3C200B3F6C3 /* rtmp_sys.h */, 99 | 76FACCB5145BB3C200B3F6C3 /* rtmp.c */, 100 | 76FACCB6145BB3C200B3F6C3 /* rtmp.h */, 101 | 76FACC9F145BB33D00B3F6C3 /* Supporting Files */, 102 | ); 103 | path = librtmp; 104 | sourceTree = ""; 105 | }; 106 | 76FACC9F145BB33D00B3F6C3 /* Supporting Files */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 76FACCA0145BB33D00B3F6C3 /* librtmp-Prefix.pch */, 110 | ); 111 | name = "Supporting Files"; 112 | sourceTree = ""; 113 | }; 114 | /* End PBXGroup section */ 115 | 116 | /* Begin PBXHeadersBuildPhase section */ 117 | 76FACC97145BB33D00B3F6C3 /* Headers */ = { 118 | isa = PBXHeadersBuildPhase; 119 | buildActionMask = 2147483647; 120 | files = ( 121 | 76FACCB8145BB3C200B3F6C3 /* amf.h in Headers */, 122 | 76FACCB9145BB3C200B3F6C3 /* bytes.h in Headers */, 123 | 76FACCBA145BB3C200B3F6C3 /* dh.h in Headers */, 124 | 76FACCBB145BB3C200B3F6C3 /* dhgroups.h in Headers */, 125 | 76FACCBC145BB3C200B3F6C3 /* handshake.h in Headers */, 126 | 76FACCBE145BB3C200B3F6C3 /* http.h in Headers */, 127 | 76FACCC0145BB3C200B3F6C3 /* log.h in Headers */, 128 | 76FACCC2145BB3C200B3F6C3 /* rtmp_sys.h in Headers */, 129 | 76FACCC4145BB3C200B3F6C3 /* rtmp.h in Headers */, 130 | ); 131 | runOnlyForDeploymentPostprocessing = 0; 132 | }; 133 | /* End PBXHeadersBuildPhase section */ 134 | 135 | /* Begin PBXNativeTarget section */ 136 | 76FACC98145BB33D00B3F6C3 /* librtmp */ = { 137 | isa = PBXNativeTarget; 138 | buildConfigurationList = 76FACCA6145BB33D00B3F6C3 /* Build configuration list for PBXNativeTarget "librtmp" */; 139 | buildPhases = ( 140 | 76FACC95145BB33D00B3F6C3 /* Sources */, 141 | 76FACC96145BB33D00B3F6C3 /* Frameworks */, 142 | 76FACC97145BB33D00B3F6C3 /* Headers */, 143 | ); 144 | buildRules = ( 145 | ); 146 | dependencies = ( 147 | ); 148 | name = librtmp; 149 | productName = librtmp; 150 | productReference = 76FACC99145BB33D00B3F6C3 /* librtmp.a */; 151 | productType = "com.apple.product-type.library.static"; 152 | }; 153 | /* End PBXNativeTarget section */ 154 | 155 | /* Begin PBXProject section */ 156 | 76FACC90145BB33D00B3F6C3 /* Project object */ = { 157 | isa = PBXProject; 158 | attributes = { 159 | LastUpgradeCheck = 0420; 160 | ORGANIZATIONNAME = "Igloo Sistemas Ltda."; 161 | }; 162 | buildConfigurationList = 76FACC93145BB33D00B3F6C3 /* Build configuration list for PBXProject "librtmp" */; 163 | compatibilityVersion = "Xcode 3.2"; 164 | developmentRegion = English; 165 | hasScannedForEncodings = 0; 166 | knownRegions = ( 167 | en, 168 | ); 169 | mainGroup = 76FACC8E145BB33D00B3F6C3; 170 | productRefGroup = 76FACC9A145BB33D00B3F6C3 /* Products */; 171 | projectDirPath = ""; 172 | projectRoot = ""; 173 | targets = ( 174 | 76FACC98145BB33D00B3F6C3 /* librtmp */, 175 | ); 176 | }; 177 | /* End PBXProject section */ 178 | 179 | /* Begin PBXSourcesBuildPhase section */ 180 | 76FACC95145BB33D00B3F6C3 /* Sources */ = { 181 | isa = PBXSourcesBuildPhase; 182 | buildActionMask = 2147483647; 183 | files = ( 184 | 76FACCB7145BB3C200B3F6C3 /* amf.c in Sources */, 185 | 76FACCBD145BB3C200B3F6C3 /* hashswf.c in Sources */, 186 | 76FACCBF145BB3C200B3F6C3 /* log.c in Sources */, 187 | 76FACCC1145BB3C200B3F6C3 /* parseurl.c in Sources */, 188 | 76FACCC3145BB3C200B3F6C3 /* rtmp.c in Sources */, 189 | ); 190 | runOnlyForDeploymentPostprocessing = 0; 191 | }; 192 | /* End PBXSourcesBuildPhase section */ 193 | 194 | /* Begin XCBuildConfiguration section */ 195 | 76FACCA4145BB33D00B3F6C3 /* Debug */ = { 196 | isa = XCBuildConfiguration; 197 | buildSettings = { 198 | ALWAYS_SEARCH_USER_PATHS = NO; 199 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 200 | COPY_PHASE_STRIP = NO; 201 | GCC_C_LANGUAGE_STANDARD = gnu99; 202 | GCC_DYNAMIC_NO_PIC = NO; 203 | GCC_OPTIMIZATION_LEVEL = 0; 204 | GCC_PREPROCESSOR_DEFINITIONS = ( 205 | NO_CRYPTO, 206 | "DEBUG=1", 207 | "$(inherited)", 208 | ); 209 | GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = ""; 210 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 211 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 212 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 213 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 214 | GCC_WARN_UNUSED_VARIABLE = YES; 215 | IPHONEOS_DEPLOYMENT_TARGET = 4.2; 216 | SDKROOT = iphoneos; 217 | VALID_ARCHS = armv7; 218 | }; 219 | name = Debug; 220 | }; 221 | 76FACCA5145BB33D00B3F6C3 /* Release */ = { 222 | isa = XCBuildConfiguration; 223 | buildSettings = { 224 | ALWAYS_SEARCH_USER_PATHS = NO; 225 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 226 | COPY_PHASE_STRIP = YES; 227 | GCC_C_LANGUAGE_STANDARD = gnu99; 228 | GCC_PREPROCESSOR_DEFINITIONS = NO_CRYPTO; 229 | GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = ""; 230 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 231 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 232 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 233 | GCC_WARN_UNUSED_VARIABLE = YES; 234 | IPHONEOS_DEPLOYMENT_TARGET = 4.2; 235 | SDKROOT = iphoneos; 236 | VALIDATE_PRODUCT = YES; 237 | VALID_ARCHS = armv7; 238 | }; 239 | name = Release; 240 | }; 241 | 76FACCA7145BB33D00B3F6C3 /* Debug */ = { 242 | isa = XCBuildConfiguration; 243 | buildSettings = { 244 | DSTROOT = /tmp/librtmp.dst; 245 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 246 | GCC_PREFIX_HEADER = "librtmp/librtmp-Prefix.pch"; 247 | OTHER_LDFLAGS = "-ObjC"; 248 | PRODUCT_NAME = rtmp; 249 | SKIP_INSTALL = YES; 250 | }; 251 | name = Debug; 252 | }; 253 | 76FACCA8145BB33D00B3F6C3 /* Release */ = { 254 | isa = XCBuildConfiguration; 255 | buildSettings = { 256 | DSTROOT = /tmp/librtmp.dst; 257 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 258 | GCC_PREFIX_HEADER = "librtmp/librtmp-Prefix.pch"; 259 | OTHER_LDFLAGS = "-ObjC"; 260 | PRODUCT_NAME = rtmp; 261 | SKIP_INSTALL = YES; 262 | }; 263 | name = Release; 264 | }; 265 | /* End XCBuildConfiguration section */ 266 | 267 | /* Begin XCConfigurationList section */ 268 | 76FACC93145BB33D00B3F6C3 /* Build configuration list for PBXProject "librtmp" */ = { 269 | isa = XCConfigurationList; 270 | buildConfigurations = ( 271 | 76FACCA4145BB33D00B3F6C3 /* Debug */, 272 | 76FACCA5145BB33D00B3F6C3 /* Release */, 273 | ); 274 | defaultConfigurationIsVisible = 0; 275 | defaultConfigurationName = Release; 276 | }; 277 | 76FACCA6145BB33D00B3F6C3 /* Build configuration list for PBXNativeTarget "librtmp" */ = { 278 | isa = XCConfigurationList; 279 | buildConfigurations = ( 280 | 76FACCA7145BB33D00B3F6C3 /* Debug */, 281 | 76FACCA8145BB33D00B3F6C3 /* Release */, 282 | ); 283 | defaultConfigurationIsVisible = 0; 284 | defaultConfigurationName = Release; 285 | }; 286 | /* End XCConfigurationList section */ 287 | }; 288 | rootObject = 76FACC90145BB33D00B3F6C3 /* Project object */; 289 | } 290 | -------------------------------------------------------------------------------- /librtmp/amf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005-2008 Team XBMC 3 | * http://www.xbmc.org 4 | * Copyright (C) 2008-2009 Andrej Stepanchuk 5 | * Copyright (C) 2009-2010 Howard Chu 6 | * 7 | * This file is part of librtmp. 8 | * 9 | * librtmp is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation; either version 2.1, 12 | * or (at your option) any later version. 13 | * 14 | * librtmp is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public License 20 | * along with librtmp see the file COPYING. If not, write to 21 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | * Boston, MA 02110-1301, USA. 23 | * http://www.gnu.org/copyleft/lgpl.html 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include "rtmp_sys.h" 31 | #include "amf.h" 32 | #include "log.h" 33 | #include "bytes.h" 34 | 35 | static const AMFObjectProperty AMFProp_Invalid = { {0, 0}, AMF_INVALID }; 36 | static const AVal AV_empty = { 0, 0 }; 37 | 38 | /* Data is Big-Endian */ 39 | unsigned short 40 | AMF_DecodeInt16(const char *data) 41 | { 42 | unsigned char *c = (unsigned char *) data; 43 | unsigned short val; 44 | val = (c[0] << 8) | c[1]; 45 | return val; 46 | } 47 | 48 | unsigned int 49 | AMF_DecodeInt24(const char *data) 50 | { 51 | unsigned char *c = (unsigned char *) data; 52 | unsigned int val; 53 | val = (c[0] << 16) | (c[1] << 8) | c[2]; 54 | return val; 55 | } 56 | 57 | unsigned int 58 | AMF_DecodeInt32(const char *data) 59 | { 60 | unsigned char *c = (unsigned char *)data; 61 | unsigned int val; 62 | val = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; 63 | return val; 64 | } 65 | 66 | void 67 | AMF_DecodeString(const char *data, AVal *bv) 68 | { 69 | bv->av_len = AMF_DecodeInt16(data); 70 | bv->av_val = (bv->av_len > 0) ? (char *)data + 2 : NULL; 71 | } 72 | 73 | void 74 | AMF_DecodeLongString(const char *data, AVal *bv) 75 | { 76 | bv->av_len = AMF_DecodeInt32(data); 77 | bv->av_val = (bv->av_len > 0) ? (char *)data + 4 : NULL; 78 | } 79 | 80 | double 81 | AMF_DecodeNumber(const char *data) 82 | { 83 | double dVal; 84 | #if __FLOAT_WORD_ORDER == __BYTE_ORDER 85 | #if __BYTE_ORDER == __BIG_ENDIAN 86 | memcpy(&dVal, data, 8); 87 | #elif __BYTE_ORDER == __LITTLE_ENDIAN 88 | unsigned char *ci, *co; 89 | ci = (unsigned char *)data; 90 | co = (unsigned char *)&dVal; 91 | co[0] = ci[7]; 92 | co[1] = ci[6]; 93 | co[2] = ci[5]; 94 | co[3] = ci[4]; 95 | co[4] = ci[3]; 96 | co[5] = ci[2]; 97 | co[6] = ci[1]; 98 | co[7] = ci[0]; 99 | #endif 100 | #else 101 | #if __BYTE_ORDER == __LITTLE_ENDIAN /* __FLOAT_WORD_ORER == __BIG_ENDIAN */ 102 | unsigned char *ci, *co; 103 | ci = (unsigned char *)data; 104 | co = (unsigned char *)&dVal; 105 | co[0] = ci[3]; 106 | co[1] = ci[2]; 107 | co[2] = ci[1]; 108 | co[3] = ci[0]; 109 | co[4] = ci[7]; 110 | co[5] = ci[6]; 111 | co[6] = ci[5]; 112 | co[7] = ci[4]; 113 | #else /* __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN */ 114 | unsigned char *ci, *co; 115 | ci = (unsigned char *)data; 116 | co = (unsigned char *)&dVal; 117 | co[0] = ci[4]; 118 | co[1] = ci[5]; 119 | co[2] = ci[6]; 120 | co[3] = ci[7]; 121 | co[4] = ci[0]; 122 | co[5] = ci[1]; 123 | co[6] = ci[2]; 124 | co[7] = ci[3]; 125 | #endif 126 | #endif 127 | return dVal; 128 | } 129 | 130 | int 131 | AMF_DecodeBoolean(const char *data) 132 | { 133 | return *data != 0; 134 | } 135 | 136 | char * 137 | AMF_EncodeInt16(char *output, char *outend, short nVal) 138 | { 139 | if (output+2 > outend) 140 | return NULL; 141 | 142 | output[1] = nVal & 0xff; 143 | output[0] = nVal >> 8; 144 | return output+2; 145 | } 146 | 147 | char * 148 | AMF_EncodeInt24(char *output, char *outend, int nVal) 149 | { 150 | if (output+3 > outend) 151 | return NULL; 152 | 153 | output[2] = nVal & 0xff; 154 | output[1] = nVal >> 8; 155 | output[0] = nVal >> 16; 156 | return output+3; 157 | } 158 | 159 | char * 160 | AMF_EncodeInt32(char *output, char *outend, int nVal) 161 | { 162 | if (output+4 > outend) 163 | return NULL; 164 | 165 | output[3] = nVal & 0xff; 166 | output[2] = nVal >> 8; 167 | output[1] = nVal >> 16; 168 | output[0] = nVal >> 24; 169 | return output+4; 170 | } 171 | 172 | char * 173 | AMF_EncodeString(char *output, char *outend, const AVal *bv) 174 | { 175 | if ((bv->av_len < 65536 && output + 1 + 2 + bv->av_len > outend) || 176 | output + 1 + 4 + bv->av_len > outend) 177 | return NULL; 178 | 179 | if (bv->av_len < 65536) 180 | { 181 | *output++ = AMF_STRING; 182 | 183 | output = AMF_EncodeInt16(output, outend, bv->av_len); 184 | } 185 | else 186 | { 187 | *output++ = AMF_LONG_STRING; 188 | 189 | output = AMF_EncodeInt32(output, outend, bv->av_len); 190 | } 191 | memcpy(output, bv->av_val, bv->av_len); 192 | output += bv->av_len; 193 | 194 | return output; 195 | } 196 | 197 | char * 198 | AMF_EncodeNumber(char *output, char *outend, double dVal) 199 | { 200 | if (output+1+8 > outend) 201 | return NULL; 202 | 203 | *output++ = AMF_NUMBER; /* type: Number */ 204 | 205 | #if __FLOAT_WORD_ORDER == __BYTE_ORDER 206 | #if __BYTE_ORDER == __BIG_ENDIAN 207 | memcpy(output, &dVal, 8); 208 | #elif __BYTE_ORDER == __LITTLE_ENDIAN 209 | { 210 | unsigned char *ci, *co; 211 | ci = (unsigned char *)&dVal; 212 | co = (unsigned char *)output; 213 | co[0] = ci[7]; 214 | co[1] = ci[6]; 215 | co[2] = ci[5]; 216 | co[3] = ci[4]; 217 | co[4] = ci[3]; 218 | co[5] = ci[2]; 219 | co[6] = ci[1]; 220 | co[7] = ci[0]; 221 | } 222 | #endif 223 | #else 224 | #if __BYTE_ORDER == __LITTLE_ENDIAN /* __FLOAT_WORD_ORER == __BIG_ENDIAN */ 225 | { 226 | unsigned char *ci, *co; 227 | ci = (unsigned char *)&dVal; 228 | co = (unsigned char *)output; 229 | co[0] = ci[3]; 230 | co[1] = ci[2]; 231 | co[2] = ci[1]; 232 | co[3] = ci[0]; 233 | co[4] = ci[7]; 234 | co[5] = ci[6]; 235 | co[6] = ci[5]; 236 | co[7] = ci[4]; 237 | } 238 | #else /* __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN */ 239 | { 240 | unsigned char *ci, *co; 241 | ci = (unsigned char *)&dVal; 242 | co = (unsigned char *)output; 243 | co[0] = ci[4]; 244 | co[1] = ci[5]; 245 | co[2] = ci[6]; 246 | co[3] = ci[7]; 247 | co[4] = ci[0]; 248 | co[5] = ci[1]; 249 | co[6] = ci[2]; 250 | co[7] = ci[3]; 251 | } 252 | #endif 253 | #endif 254 | 255 | return output+8; 256 | } 257 | 258 | char * 259 | AMF_EncodeBoolean(char *output, char *outend, int bVal) 260 | { 261 | if (output+2 > outend) 262 | return NULL; 263 | 264 | *output++ = AMF_BOOLEAN; 265 | 266 | *output++ = bVal ? 0x01 : 0x00; 267 | 268 | return output; 269 | } 270 | 271 | char * 272 | AMF_EncodeNamedString(char *output, char *outend, const AVal *strName, const AVal *strValue) 273 | { 274 | if (output+2+strName->av_len > outend) 275 | return NULL; 276 | output = AMF_EncodeInt16(output, outend, strName->av_len); 277 | 278 | memcpy(output, strName->av_val, strName->av_len); 279 | output += strName->av_len; 280 | 281 | return AMF_EncodeString(output, outend, strValue); 282 | } 283 | 284 | char * 285 | AMF_EncodeNamedNumber(char *output, char *outend, const AVal *strName, double dVal) 286 | { 287 | if (output+2+strName->av_len > outend) 288 | return NULL; 289 | output = AMF_EncodeInt16(output, outend, strName->av_len); 290 | 291 | memcpy(output, strName->av_val, strName->av_len); 292 | output += strName->av_len; 293 | 294 | return AMF_EncodeNumber(output, outend, dVal); 295 | } 296 | 297 | char * 298 | AMF_EncodeNamedBoolean(char *output, char *outend, const AVal *strName, int bVal) 299 | { 300 | if (output+2+strName->av_len > outend) 301 | return NULL; 302 | output = AMF_EncodeInt16(output, outend, strName->av_len); 303 | 304 | memcpy(output, strName->av_val, strName->av_len); 305 | output += strName->av_len; 306 | 307 | return AMF_EncodeBoolean(output, outend, bVal); 308 | } 309 | 310 | void 311 | AMFProp_GetName(AMFObjectProperty *prop, AVal *name) 312 | { 313 | *name = prop->p_name; 314 | } 315 | 316 | void 317 | AMFProp_SetName(AMFObjectProperty *prop, AVal *name) 318 | { 319 | prop->p_name = *name; 320 | } 321 | 322 | AMFDataType 323 | AMFProp_GetType(AMFObjectProperty *prop) 324 | { 325 | return prop->p_type; 326 | } 327 | 328 | double 329 | AMFProp_GetNumber(AMFObjectProperty *prop) 330 | { 331 | return prop->p_vu.p_number; 332 | } 333 | 334 | int 335 | AMFProp_GetBoolean(AMFObjectProperty *prop) 336 | { 337 | return prop->p_vu.p_number != 0; 338 | } 339 | 340 | void 341 | AMFProp_GetString(AMFObjectProperty *prop, AVal *str) 342 | { 343 | *str = prop->p_vu.p_aval; 344 | } 345 | 346 | void 347 | AMFProp_GetObject(AMFObjectProperty *prop, AMFObject *obj) 348 | { 349 | *obj = prop->p_vu.p_object; 350 | } 351 | 352 | int 353 | AMFProp_IsValid(AMFObjectProperty *prop) 354 | { 355 | return prop->p_type != AMF_INVALID; 356 | } 357 | 358 | char * 359 | AMFProp_Encode(AMFObjectProperty *prop, char *pBuffer, char *pBufEnd) 360 | { 361 | if (prop->p_type == AMF_INVALID) 362 | return NULL; 363 | 364 | if (prop->p_type != AMF_NULL && pBuffer + prop->p_name.av_len + 2 + 1 >= pBufEnd) 365 | return NULL; 366 | 367 | if (prop->p_type != AMF_NULL && prop->p_name.av_len) 368 | { 369 | *pBuffer++ = prop->p_name.av_len >> 8; 370 | *pBuffer++ = prop->p_name.av_len & 0xff; 371 | memcpy(pBuffer, prop->p_name.av_val, prop->p_name.av_len); 372 | pBuffer += prop->p_name.av_len; 373 | } 374 | 375 | switch (prop->p_type) 376 | { 377 | case AMF_NUMBER: 378 | pBuffer = AMF_EncodeNumber(pBuffer, pBufEnd, prop->p_vu.p_number); 379 | break; 380 | 381 | case AMF_BOOLEAN: 382 | pBuffer = AMF_EncodeBoolean(pBuffer, pBufEnd, prop->p_vu.p_number != 0); 383 | break; 384 | 385 | case AMF_STRING: 386 | pBuffer = AMF_EncodeString(pBuffer, pBufEnd, &prop->p_vu.p_aval); 387 | break; 388 | 389 | case AMF_NULL: 390 | if (pBuffer+1 >= pBufEnd) 391 | return NULL; 392 | *pBuffer++ = AMF_NULL; 393 | break; 394 | 395 | case AMF_OBJECT: 396 | pBuffer = AMF_Encode(&prop->p_vu.p_object, pBuffer, pBufEnd); 397 | break; 398 | 399 | default: 400 | RTMP_Log(RTMP_LOGERROR, "%s, invalid type. %d", __FUNCTION__, prop->p_type); 401 | pBuffer = NULL; 402 | }; 403 | 404 | return pBuffer; 405 | } 406 | 407 | #define AMF3_INTEGER_MAX 268435455 408 | #define AMF3_INTEGER_MIN -268435456 409 | 410 | int 411 | AMF3ReadInteger(const char *data, int32_t *valp) 412 | { 413 | int i = 0; 414 | int32_t val = 0; 415 | 416 | while (i <= 2) 417 | { /* handle first 3 bytes */ 418 | if (data[i] & 0x80) 419 | { /* byte used */ 420 | val <<= 7; /* shift up */ 421 | val |= (data[i] & 0x7f); /* add bits */ 422 | i++; 423 | } 424 | else 425 | { 426 | break; 427 | } 428 | } 429 | 430 | if (i > 2) 431 | { /* use 4th byte, all 8bits */ 432 | val <<= 8; 433 | val |= data[3]; 434 | 435 | /* range check */ 436 | if (val > AMF3_INTEGER_MAX) 437 | val -= (1 << 29); 438 | } 439 | else 440 | { /* use 7bits of last unparsed byte (0xxxxxxx) */ 441 | val <<= 7; 442 | val |= data[i]; 443 | } 444 | 445 | *valp = val; 446 | 447 | return i > 2 ? 4 : i + 1; 448 | } 449 | 450 | int 451 | AMF3ReadString(const char *data, AVal *str) 452 | { 453 | int32_t ref = 0; 454 | int len; 455 | assert(str != 0); 456 | 457 | len = AMF3ReadInteger(data, &ref); 458 | data += len; 459 | 460 | if ((ref & 0x1) == 0) 461 | { /* reference: 0xxx */ 462 | uint32_t refIndex = (ref >> 1); 463 | RTMP_Log(RTMP_LOGDEBUG, 464 | "%s, string reference, index: %d, not supported, ignoring!", 465 | __FUNCTION__, refIndex); 466 | return len; 467 | } 468 | else 469 | { 470 | uint32_t nSize = (ref >> 1); 471 | 472 | str->av_val = (char *)data; 473 | str->av_len = nSize; 474 | 475 | return len + nSize; 476 | } 477 | return len; 478 | } 479 | 480 | int 481 | AMF3Prop_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize, 482 | int bDecodeName) 483 | { 484 | int nOriginalSize = nSize; 485 | AMF3DataType type; 486 | 487 | prop->p_name.av_len = 0; 488 | prop->p_name.av_val = NULL; 489 | 490 | if (nSize == 0 || !pBuffer) 491 | { 492 | RTMP_Log(RTMP_LOGDEBUG, "empty buffer/no buffer pointer!"); 493 | return -1; 494 | } 495 | 496 | /* decode name */ 497 | if (bDecodeName) 498 | { 499 | AVal name; 500 | int nRes = AMF3ReadString(pBuffer, &name); 501 | 502 | if (name.av_len <= 0) 503 | return nRes; 504 | 505 | prop->p_name = name; 506 | pBuffer += nRes; 507 | nSize -= nRes; 508 | } 509 | 510 | /* decode */ 511 | type = *pBuffer++; 512 | nSize--; 513 | 514 | switch (type) 515 | { 516 | case AMF3_UNDEFINED: 517 | case AMF3_NULL: 518 | prop->p_type = AMF_NULL; 519 | break; 520 | case AMF3_FALSE: 521 | prop->p_type = AMF_BOOLEAN; 522 | prop->p_vu.p_number = 0.0; 523 | break; 524 | case AMF3_TRUE: 525 | prop->p_type = AMF_BOOLEAN; 526 | prop->p_vu.p_number = 1.0; 527 | break; 528 | case AMF3_INTEGER: 529 | { 530 | int32_t res = 0; 531 | int len = AMF3ReadInteger(pBuffer, &res); 532 | prop->p_vu.p_number = (double)res; 533 | prop->p_type = AMF_NUMBER; 534 | nSize -= len; 535 | break; 536 | } 537 | case AMF3_DOUBLE: 538 | if (nSize < 8) 539 | return -1; 540 | prop->p_vu.p_number = AMF_DecodeNumber(pBuffer); 541 | prop->p_type = AMF_NUMBER; 542 | nSize -= 8; 543 | break; 544 | case AMF3_STRING: 545 | case AMF3_XML_DOC: 546 | case AMF3_XML: 547 | { 548 | int len = AMF3ReadString(pBuffer, &prop->p_vu.p_aval); 549 | prop->p_type = AMF_STRING; 550 | nSize -= len; 551 | break; 552 | } 553 | case AMF3_DATE: 554 | { 555 | int32_t res = 0; 556 | int len = AMF3ReadInteger(pBuffer, &res); 557 | 558 | nSize -= len; 559 | pBuffer += len; 560 | 561 | if ((res & 0x1) == 0) 562 | { /* reference */ 563 | uint32_t nIndex = (res >> 1); 564 | RTMP_Log(RTMP_LOGDEBUG, "AMF3_DATE reference: %d, not supported!", nIndex); 565 | } 566 | else 567 | { 568 | if (nSize < 8) 569 | return -1; 570 | 571 | prop->p_vu.p_number = AMF_DecodeNumber(pBuffer); 572 | nSize -= 8; 573 | prop->p_type = AMF_NUMBER; 574 | } 575 | break; 576 | } 577 | case AMF3_OBJECT: 578 | { 579 | int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE); 580 | if (nRes == -1) 581 | return -1; 582 | nSize -= nRes; 583 | prop->p_type = AMF_OBJECT; 584 | break; 585 | } 586 | case AMF3_ARRAY: 587 | case AMF3_BYTE_ARRAY: 588 | default: 589 | RTMP_Log(RTMP_LOGDEBUG, "%s - AMF3 unknown/unsupported datatype 0x%02x, @0x%08X", 590 | __FUNCTION__, (unsigned char)(*pBuffer), pBuffer); 591 | return -1; 592 | } 593 | 594 | return nOriginalSize - nSize; 595 | } 596 | 597 | int 598 | AMFProp_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize, 599 | int bDecodeName) 600 | { 601 | int nOriginalSize = nSize; 602 | int nRes; 603 | 604 | prop->p_name.av_len = 0; 605 | prop->p_name.av_val = NULL; 606 | 607 | if (nSize == 0 || !pBuffer) 608 | { 609 | RTMP_Log(RTMP_LOGDEBUG, "%s: Empty buffer/no buffer pointer!", __FUNCTION__); 610 | return -1; 611 | } 612 | 613 | if (bDecodeName && nSize < 4) 614 | { /* at least name (length + at least 1 byte) and 1 byte of data */ 615 | RTMP_Log(RTMP_LOGDEBUG, 616 | "%s: Not enough data for decoding with name, less than 4 bytes!", 617 | __FUNCTION__); 618 | return -1; 619 | } 620 | 621 | if (bDecodeName) 622 | { 623 | unsigned short nNameSize = AMF_DecodeInt16(pBuffer); 624 | if (nNameSize > nSize - 2) 625 | { 626 | RTMP_Log(RTMP_LOGDEBUG, 627 | "%s: Name size out of range: namesize (%d) > len (%d) - 2", 628 | __FUNCTION__, nNameSize, nSize); 629 | return -1; 630 | } 631 | 632 | AMF_DecodeString(pBuffer, &prop->p_name); 633 | nSize -= 2 + nNameSize; 634 | pBuffer += 2 + nNameSize; 635 | } 636 | 637 | if (nSize == 0) 638 | { 639 | return -1; 640 | } 641 | 642 | nSize--; 643 | 644 | prop->p_type = *pBuffer++; 645 | switch (prop->p_type) 646 | { 647 | case AMF_NUMBER: 648 | if (nSize < 8) 649 | return -1; 650 | prop->p_vu.p_number = AMF_DecodeNumber(pBuffer); 651 | nSize -= 8; 652 | break; 653 | case AMF_BOOLEAN: 654 | if (nSize < 1) 655 | return -1; 656 | prop->p_vu.p_number = (double)AMF_DecodeBoolean(pBuffer); 657 | nSize--; 658 | break; 659 | case AMF_STRING: 660 | { 661 | unsigned short nStringSize = AMF_DecodeInt16(pBuffer); 662 | 663 | if (nSize < (long)nStringSize + 2) 664 | return -1; 665 | AMF_DecodeString(pBuffer, &prop->p_vu.p_aval); 666 | nSize -= (2 + nStringSize); 667 | break; 668 | } 669 | case AMF_OBJECT: 670 | { 671 | int nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE); 672 | if (nRes == -1) 673 | return -1; 674 | nSize -= nRes; 675 | break; 676 | } 677 | case AMF_MOVIECLIP: 678 | { 679 | RTMP_Log(RTMP_LOGERROR, "AMF_MOVIECLIP reserved!"); 680 | return -1; 681 | break; 682 | } 683 | case AMF_NULL: 684 | case AMF_UNDEFINED: 685 | case AMF_UNSUPPORTED: 686 | prop->p_type = AMF_NULL; 687 | break; 688 | case AMF_REFERENCE: 689 | { 690 | RTMP_Log(RTMP_LOGERROR, "AMF_REFERENCE not supported!"); 691 | return -1; 692 | break; 693 | } 694 | case AMF_ECMA_ARRAY: 695 | { 696 | nSize -= 4; 697 | 698 | /* next comes the rest, mixed array has a final 0x000009 mark and names, so its an object */ 699 | nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer + 4, nSize, TRUE); 700 | if (nRes == -1) 701 | return -1; 702 | nSize -= nRes; 703 | prop->p_type = AMF_OBJECT; 704 | break; 705 | } 706 | case AMF_OBJECT_END: 707 | { 708 | return -1; 709 | break; 710 | } 711 | case AMF_STRICT_ARRAY: 712 | { 713 | unsigned int nArrayLen = AMF_DecodeInt32(pBuffer); 714 | nSize -= 4; 715 | 716 | nRes = AMF_DecodeArray(&prop->p_vu.p_object, pBuffer + 4, nSize, 717 | nArrayLen, FALSE); 718 | if (nRes == -1) 719 | return -1; 720 | nSize -= nRes; 721 | prop->p_type = AMF_OBJECT; 722 | break; 723 | } 724 | case AMF_DATE: 725 | { 726 | RTMP_Log(RTMP_LOGDEBUG, "AMF_DATE"); 727 | 728 | if (nSize < 10) 729 | return -1; 730 | 731 | prop->p_vu.p_number = AMF_DecodeNumber(pBuffer); 732 | prop->p_UTCoffset = AMF_DecodeInt16(pBuffer + 8); 733 | 734 | nSize -= 10; 735 | break; 736 | } 737 | case AMF_LONG_STRING: 738 | { 739 | unsigned int nStringSize = AMF_DecodeInt32(pBuffer); 740 | if (nSize < (long)nStringSize + 4) 741 | return -1; 742 | AMF_DecodeLongString(pBuffer, &prop->p_vu.p_aval); 743 | nSize -= (4 + nStringSize); 744 | prop->p_type = AMF_STRING; 745 | break; 746 | } 747 | case AMF_RECORDSET: 748 | { 749 | RTMP_Log(RTMP_LOGERROR, "AMF_RECORDSET reserved!"); 750 | return -1; 751 | break; 752 | } 753 | case AMF_XML_DOC: 754 | { 755 | RTMP_Log(RTMP_LOGERROR, "AMF_XML_DOC not supported!"); 756 | return -1; 757 | break; 758 | } 759 | case AMF_TYPED_OBJECT: 760 | { 761 | RTMP_Log(RTMP_LOGERROR, "AMF_TYPED_OBJECT not supported!"); 762 | return -1; 763 | break; 764 | } 765 | case AMF_AVMPLUS: 766 | { 767 | int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE); 768 | if (nRes == -1) 769 | return -1; 770 | nSize -= nRes; 771 | prop->p_type = AMF_OBJECT; 772 | break; 773 | } 774 | default: 775 | RTMP_Log(RTMP_LOGDEBUG, "%s - unknown datatype 0x%02x, @0x%08X", __FUNCTION__, 776 | prop->p_type, pBuffer - 1); 777 | return -1; 778 | } 779 | 780 | return nOriginalSize - nSize; 781 | } 782 | 783 | void 784 | AMFProp_Dump(AMFObjectProperty *prop) 785 | { 786 | char strRes[256]; 787 | char str[256]; 788 | AVal name; 789 | 790 | if (prop->p_type == AMF_INVALID) 791 | { 792 | RTMP_Log(RTMP_LOGDEBUG, "Property: INVALID"); 793 | return; 794 | } 795 | 796 | if (prop->p_type == AMF_NULL) 797 | { 798 | RTMP_Log(RTMP_LOGDEBUG, "Property: NULL"); 799 | return; 800 | } 801 | 802 | if (prop->p_name.av_len) 803 | { 804 | name = prop->p_name; 805 | } 806 | else 807 | { 808 | name.av_val = "no-name."; 809 | name.av_len = sizeof("no-name.") - 1; 810 | } 811 | if (name.av_len > 18) 812 | name.av_len = 18; 813 | 814 | snprintf(strRes, 255, "Name: %18.*s, ", name.av_len, name.av_val); 815 | 816 | if (prop->p_type == AMF_OBJECT) 817 | { 818 | RTMP_Log(RTMP_LOGDEBUG, "Property: <%sOBJECT>", strRes); 819 | AMF_Dump(&prop->p_vu.p_object); 820 | return; 821 | } 822 | 823 | switch (prop->p_type) 824 | { 825 | case AMF_NUMBER: 826 | snprintf(str, 255, "NUMBER:\t%.2f", prop->p_vu.p_number); 827 | break; 828 | case AMF_BOOLEAN: 829 | snprintf(str, 255, "BOOLEAN:\t%s", 830 | prop->p_vu.p_number != 0.0 ? "TRUE" : "FALSE"); 831 | break; 832 | case AMF_STRING: 833 | snprintf(str, 255, "STRING:\t%.*s", prop->p_vu.p_aval.av_len, 834 | prop->p_vu.p_aval.av_val); 835 | break; 836 | case AMF_DATE: 837 | snprintf(str, 255, "DATE:\ttimestamp: %.2f, UTC offset: %d", 838 | prop->p_vu.p_number, prop->p_UTCoffset); 839 | break; 840 | default: 841 | snprintf(str, 255, "INVALID TYPE 0x%02x", (unsigned char)prop->p_type); 842 | } 843 | 844 | RTMP_Log(RTMP_LOGDEBUG, "Property: <%s%s>", strRes, str); 845 | } 846 | 847 | void 848 | AMFProp_Reset(AMFObjectProperty *prop) 849 | { 850 | if (prop->p_type == AMF_OBJECT) 851 | AMF_Reset(&prop->p_vu.p_object); 852 | else 853 | { 854 | prop->p_vu.p_aval.av_len = 0; 855 | prop->p_vu.p_aval.av_val = NULL; 856 | } 857 | prop->p_type = AMF_INVALID; 858 | } 859 | 860 | /* AMFObject */ 861 | 862 | char * 863 | AMF_Encode(AMFObject *obj, char *pBuffer, char *pBufEnd) 864 | { 865 | int i; 866 | 867 | if (pBuffer+4 >= pBufEnd) 868 | return NULL; 869 | 870 | *pBuffer++ = AMF_OBJECT; 871 | 872 | for (i = 0; i < obj->o_num; i++) 873 | { 874 | char *res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd); 875 | if (res == NULL) 876 | { 877 | RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d", 878 | i); 879 | break; 880 | } 881 | else 882 | { 883 | pBuffer = res; 884 | } 885 | } 886 | 887 | if (pBuffer + 3 >= pBufEnd) 888 | return NULL; /* no room for the end marker */ 889 | 890 | pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END); 891 | 892 | return pBuffer; 893 | } 894 | 895 | int 896 | AMF_DecodeArray(AMFObject *obj, const char *pBuffer, int nSize, 897 | int nArrayLen, int bDecodeName) 898 | { 899 | int nOriginalSize = nSize; 900 | int bError = FALSE; 901 | 902 | obj->o_num = 0; 903 | obj->o_props = NULL; 904 | while (nArrayLen > 0) 905 | { 906 | AMFObjectProperty prop; 907 | int nRes; 908 | nArrayLen--; 909 | 910 | nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName); 911 | if (nRes == -1) 912 | bError = TRUE; 913 | else 914 | { 915 | nSize -= nRes; 916 | pBuffer += nRes; 917 | AMF_AddProp(obj, &prop); 918 | } 919 | } 920 | if (bError) 921 | return -1; 922 | 923 | return nOriginalSize - nSize; 924 | } 925 | 926 | int 927 | AMF3_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bAMFData) 928 | { 929 | int nOriginalSize = nSize; 930 | int32_t ref; 931 | int len; 932 | 933 | obj->o_num = 0; 934 | obj->o_props = NULL; 935 | if (bAMFData) 936 | { 937 | if (*pBuffer != AMF3_OBJECT) 938 | RTMP_Log(RTMP_LOGERROR, 939 | "AMF3 Object encapsulated in AMF stream does not start with AMF3_OBJECT!"); 940 | pBuffer++; 941 | nSize--; 942 | } 943 | 944 | ref = 0; 945 | len = AMF3ReadInteger(pBuffer, &ref); 946 | pBuffer += len; 947 | nSize -= len; 948 | 949 | if ((ref & 1) == 0) 950 | { /* object reference, 0xxx */ 951 | uint32_t objectIndex = (ref >> 1); 952 | 953 | RTMP_Log(RTMP_LOGDEBUG, "Object reference, index: %d", objectIndex); 954 | } 955 | else /* object instance */ 956 | { 957 | int32_t classRef = (ref >> 1); 958 | 959 | AMF3ClassDef cd = { {0, 0} 960 | }; 961 | AMFObjectProperty prop; 962 | 963 | if ((classRef & 0x1) == 0) 964 | { /* class reference */ 965 | uint32_t classIndex = (classRef >> 1); 966 | RTMP_Log(RTMP_LOGDEBUG, "Class reference: %d", classIndex); 967 | } 968 | else 969 | { 970 | int32_t classExtRef = (classRef >> 1); 971 | int i; 972 | 973 | cd.cd_externalizable = (classExtRef & 0x1) == 1; 974 | cd.cd_dynamic = ((classExtRef >> 1) & 0x1) == 1; 975 | 976 | cd.cd_num = classExtRef >> 2; 977 | 978 | /* class name */ 979 | 980 | len = AMF3ReadString(pBuffer, &cd.cd_name); 981 | nSize -= len; 982 | pBuffer += len; 983 | 984 | /*std::string str = className; */ 985 | 986 | RTMP_Log(RTMP_LOGDEBUG, 987 | "Class name: %s, externalizable: %d, dynamic: %d, classMembers: %d", 988 | cd.cd_name.av_val, cd.cd_externalizable, cd.cd_dynamic, 989 | cd.cd_num); 990 | 991 | for (i = 0; i < cd.cd_num; i++) 992 | { 993 | AVal memberName; 994 | len = AMF3ReadString(pBuffer, &memberName); 995 | RTMP_Log(RTMP_LOGDEBUG, "Member: %s", memberName.av_val); 996 | AMF3CD_AddProp(&cd, &memberName); 997 | nSize -= len; 998 | pBuffer += len; 999 | } 1000 | } 1001 | 1002 | /* add as referencable object */ 1003 | 1004 | if (cd.cd_externalizable) 1005 | { 1006 | int nRes; 1007 | AVal name = AVC("DEFAULT_ATTRIBUTE"); 1008 | 1009 | RTMP_Log(RTMP_LOGDEBUG, "Externalizable, TODO check"); 1010 | 1011 | nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE); 1012 | if (nRes == -1) 1013 | RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!", 1014 | __FUNCTION__); 1015 | else 1016 | { 1017 | nSize -= nRes; 1018 | pBuffer += nRes; 1019 | } 1020 | 1021 | AMFProp_SetName(&prop, &name); 1022 | AMF_AddProp(obj, &prop); 1023 | } 1024 | else 1025 | { 1026 | int nRes, i; 1027 | for (i = 0; i < cd.cd_num; i++) /* non-dynamic */ 1028 | { 1029 | nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE); 1030 | if (nRes == -1) 1031 | RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!", 1032 | __FUNCTION__); 1033 | 1034 | AMFProp_SetName(&prop, AMF3CD_GetProp(&cd, i)); 1035 | AMF_AddProp(obj, &prop); 1036 | 1037 | pBuffer += nRes; 1038 | nSize -= nRes; 1039 | } 1040 | if (cd.cd_dynamic) 1041 | { 1042 | int len = 0; 1043 | 1044 | do 1045 | { 1046 | nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, TRUE); 1047 | AMF_AddProp(obj, &prop); 1048 | 1049 | pBuffer += nRes; 1050 | nSize -= nRes; 1051 | 1052 | len = prop.p_name.av_len; 1053 | } 1054 | while (len > 0); 1055 | } 1056 | } 1057 | RTMP_Log(RTMP_LOGDEBUG, "class object!"); 1058 | } 1059 | return nOriginalSize - nSize; 1060 | } 1061 | 1062 | int 1063 | AMF_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bDecodeName) 1064 | { 1065 | int nOriginalSize = nSize; 1066 | int bError = FALSE; /* if there is an error while decoding - try to at least find the end mark AMF_OBJECT_END */ 1067 | 1068 | obj->o_num = 0; 1069 | obj->o_props = NULL; 1070 | while (nSize > 0) 1071 | { 1072 | AMFObjectProperty prop; 1073 | int nRes; 1074 | 1075 | if (nSize >=3 && AMF_DecodeInt24(pBuffer) == AMF_OBJECT_END) 1076 | { 1077 | nSize -= 3; 1078 | bError = FALSE; 1079 | break; 1080 | } 1081 | 1082 | if (bError) 1083 | { 1084 | RTMP_Log(RTMP_LOGERROR, 1085 | "DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!"); 1086 | nSize--; 1087 | pBuffer++; 1088 | continue; 1089 | } 1090 | 1091 | nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName); 1092 | if (nRes == -1) 1093 | bError = TRUE; 1094 | else 1095 | { 1096 | nSize -= nRes; 1097 | pBuffer += nRes; 1098 | AMF_AddProp(obj, &prop); 1099 | } 1100 | } 1101 | 1102 | if (bError) 1103 | return -1; 1104 | 1105 | return nOriginalSize - nSize; 1106 | } 1107 | 1108 | void 1109 | AMF_AddProp(AMFObject *obj, const AMFObjectProperty *prop) 1110 | { 1111 | if (!(obj->o_num & 0x0f)) 1112 | obj->o_props = 1113 | realloc(obj->o_props, (obj->o_num + 16) * sizeof(AMFObjectProperty)); 1114 | memcpy(&obj->o_props[obj->o_num++], prop, sizeof(AMFObjectProperty)); 1115 | } 1116 | 1117 | int 1118 | AMF_CountProp(AMFObject *obj) 1119 | { 1120 | return obj->o_num; 1121 | } 1122 | 1123 | AMFObjectProperty * 1124 | AMF_GetProp(AMFObject *obj, const AVal *name, int nIndex) 1125 | { 1126 | if (nIndex >= 0) 1127 | { 1128 | if (nIndex <= obj->o_num) 1129 | return &obj->o_props[nIndex]; 1130 | } 1131 | else 1132 | { 1133 | int n; 1134 | for (n = 0; n < obj->o_num; n++) 1135 | { 1136 | if (AVMATCH(&obj->o_props[n].p_name, name)) 1137 | return &obj->o_props[n]; 1138 | } 1139 | } 1140 | 1141 | return (AMFObjectProperty *)&AMFProp_Invalid; 1142 | } 1143 | 1144 | void 1145 | AMF_Dump(AMFObject *obj) 1146 | { 1147 | int n; 1148 | RTMP_Log(RTMP_LOGDEBUG, "(object begin)"); 1149 | for (n = 0; n < obj->o_num; n++) 1150 | { 1151 | AMFProp_Dump(&obj->o_props[n]); 1152 | } 1153 | RTMP_Log(RTMP_LOGDEBUG, "(object end)"); 1154 | } 1155 | 1156 | void 1157 | AMF_Reset(AMFObject *obj) 1158 | { 1159 | int n; 1160 | for (n = 0; n < obj->o_num; n++) 1161 | { 1162 | AMFProp_Reset(&obj->o_props[n]); 1163 | } 1164 | free(obj->o_props); 1165 | obj->o_props = NULL; 1166 | obj->o_num = 0; 1167 | } 1168 | 1169 | 1170 | /* AMF3ClassDefinition */ 1171 | 1172 | void 1173 | AMF3CD_AddProp(AMF3ClassDef *cd, AVal *prop) 1174 | { 1175 | if (!(cd->cd_num & 0x0f)) 1176 | cd->cd_props = realloc(cd->cd_props, (cd->cd_num + 16) * sizeof(AVal)); 1177 | cd->cd_props[cd->cd_num++] = *prop; 1178 | } 1179 | 1180 | AVal * 1181 | AMF3CD_GetProp(AMF3ClassDef *cd, int nIndex) 1182 | { 1183 | if (nIndex >= cd->cd_num) 1184 | return (AVal *)&AV_empty; 1185 | return &cd->cd_props[nIndex]; 1186 | } 1187 | -------------------------------------------------------------------------------- /librtmp/amf.h: -------------------------------------------------------------------------------- 1 | #ifndef __AMF_H__ 2 | #define __AMF_H__ 3 | /* 4 | * Copyright (C) 2005-2008 Team XBMC 5 | * http://www.xbmc.org 6 | * Copyright (C) 2008-2009 Andrej Stepanchuk 7 | * Copyright (C) 2009-2010 Howard Chu 8 | * 9 | * This file is part of librtmp. 10 | * 11 | * librtmp is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU Lesser General Public License as 13 | * published by the Free Software Foundation; either version 2.1, 14 | * or (at your option) any later version. 15 | * 16 | * librtmp is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Lesser General Public License 22 | * along with librtmp see the file COPYING. If not, write to 23 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24 | * Boston, MA 02110-1301, USA. 25 | * http://www.gnu.org/copyleft/lgpl.html 26 | */ 27 | 28 | #include 29 | 30 | #ifndef TRUE 31 | #define TRUE 1 32 | #define FALSE 0 33 | #endif 34 | 35 | #ifdef __cplusplus 36 | extern "C" 37 | { 38 | #endif 39 | 40 | typedef enum 41 | { AMF_NUMBER = 0, AMF_BOOLEAN, AMF_STRING, AMF_OBJECT, 42 | AMF_MOVIECLIP, /* reserved, not used */ 43 | AMF_NULL, AMF_UNDEFINED, AMF_REFERENCE, AMF_ECMA_ARRAY, AMF_OBJECT_END, 44 | AMF_STRICT_ARRAY, AMF_DATE, AMF_LONG_STRING, AMF_UNSUPPORTED, 45 | AMF_RECORDSET, /* reserved, not used */ 46 | AMF_XML_DOC, AMF_TYPED_OBJECT, 47 | AMF_AVMPLUS, /* switch to AMF3 */ 48 | AMF_INVALID = 0xff 49 | } AMFDataType; 50 | 51 | typedef enum 52 | { AMF3_UNDEFINED = 0, AMF3_NULL, AMF3_FALSE, AMF3_TRUE, 53 | AMF3_INTEGER, AMF3_DOUBLE, AMF3_STRING, AMF3_XML_DOC, AMF3_DATE, 54 | AMF3_ARRAY, AMF3_OBJECT, AMF3_XML, AMF3_BYTE_ARRAY 55 | } AMF3DataType; 56 | 57 | typedef struct AVal 58 | { 59 | char *av_val; 60 | int av_len; 61 | } AVal; 62 | #define AVC(str) {str,sizeof(str)-1} 63 | #define AVMATCH(a1,a2) ((a1)->av_len == (a2)->av_len && !memcmp((a1)->av_val,(a2)->av_val,(a1)->av_len)) 64 | 65 | struct AMFObjectProperty; 66 | 67 | typedef struct AMFObject 68 | { 69 | int o_num; 70 | struct AMFObjectProperty *o_props; 71 | } AMFObject; 72 | 73 | typedef struct AMFObjectProperty 74 | { 75 | AVal p_name; 76 | AMFDataType p_type; 77 | union 78 | { 79 | double p_number; 80 | AVal p_aval; 81 | AMFObject p_object; 82 | } p_vu; 83 | int16_t p_UTCoffset; 84 | } AMFObjectProperty; 85 | 86 | char *AMF_EncodeString(char *output, char *outend, const AVal * str); 87 | char *AMF_EncodeNumber(char *output, char *outend, double dVal); 88 | char *AMF_EncodeInt16(char *output, char *outend, short nVal); 89 | char *AMF_EncodeInt24(char *output, char *outend, int nVal); 90 | char *AMF_EncodeInt32(char *output, char *outend, int nVal); 91 | char *AMF_EncodeBoolean(char *output, char *outend, int bVal); 92 | 93 | /* Shortcuts for AMFProp_Encode */ 94 | char *AMF_EncodeNamedString(char *output, char *outend, const AVal * name, const AVal * value); 95 | char *AMF_EncodeNamedNumber(char *output, char *outend, const AVal * name, double dVal); 96 | char *AMF_EncodeNamedBoolean(char *output, char *outend, const AVal * name, int bVal); 97 | 98 | unsigned short AMF_DecodeInt16(const char *data); 99 | unsigned int AMF_DecodeInt24(const char *data); 100 | unsigned int AMF_DecodeInt32(const char *data); 101 | void AMF_DecodeString(const char *data, AVal * str); 102 | void AMF_DecodeLongString(const char *data, AVal * str); 103 | int AMF_DecodeBoolean(const char *data); 104 | double AMF_DecodeNumber(const char *data); 105 | 106 | char *AMF_Encode(AMFObject * obj, char *pBuffer, char *pBufEnd); 107 | int AMF_Decode(AMFObject * obj, const char *pBuffer, int nSize, 108 | int bDecodeName); 109 | int AMF_DecodeArray(AMFObject * obj, const char *pBuffer, int nSize, 110 | int nArrayLen, int bDecodeName); 111 | int AMF3_Decode(AMFObject * obj, const char *pBuffer, int nSize, 112 | int bDecodeName); 113 | void AMF_Dump(AMFObject * obj); 114 | void AMF_Reset(AMFObject * obj); 115 | 116 | void AMF_AddProp(AMFObject * obj, const AMFObjectProperty * prop); 117 | int AMF_CountProp(AMFObject * obj); 118 | AMFObjectProperty *AMF_GetProp(AMFObject * obj, const AVal * name, 119 | int nIndex); 120 | 121 | AMFDataType AMFProp_GetType(AMFObjectProperty * prop); 122 | void AMFProp_SetNumber(AMFObjectProperty * prop, double dval); 123 | void AMFProp_SetBoolean(AMFObjectProperty * prop, int bflag); 124 | void AMFProp_SetString(AMFObjectProperty * prop, AVal * str); 125 | void AMFProp_SetObject(AMFObjectProperty * prop, AMFObject * obj); 126 | 127 | void AMFProp_GetName(AMFObjectProperty * prop, AVal * name); 128 | void AMFProp_SetName(AMFObjectProperty * prop, AVal * name); 129 | double AMFProp_GetNumber(AMFObjectProperty * prop); 130 | int AMFProp_GetBoolean(AMFObjectProperty * prop); 131 | void AMFProp_GetString(AMFObjectProperty * prop, AVal * str); 132 | void AMFProp_GetObject(AMFObjectProperty * prop, AMFObject * obj); 133 | 134 | int AMFProp_IsValid(AMFObjectProperty * prop); 135 | 136 | char *AMFProp_Encode(AMFObjectProperty * prop, char *pBuffer, char *pBufEnd); 137 | int AMF3Prop_Decode(AMFObjectProperty * prop, const char *pBuffer, 138 | int nSize, int bDecodeName); 139 | int AMFProp_Decode(AMFObjectProperty * prop, const char *pBuffer, 140 | int nSize, int bDecodeName); 141 | 142 | void AMFProp_Dump(AMFObjectProperty * prop); 143 | void AMFProp_Reset(AMFObjectProperty * prop); 144 | 145 | typedef struct AMF3ClassDef 146 | { 147 | AVal cd_name; 148 | char cd_externalizable; 149 | char cd_dynamic; 150 | int cd_num; 151 | AVal *cd_props; 152 | } AMF3ClassDef; 153 | 154 | void AMF3CD_AddProp(AMF3ClassDef * cd, AVal * prop); 155 | AVal *AMF3CD_GetProp(AMF3ClassDef * cd, int idx); 156 | 157 | #ifdef __cplusplus 158 | } 159 | #endif 160 | 161 | #endif /* __AMF_H__ */ 162 | -------------------------------------------------------------------------------- /librtmp/bytes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005-2008 Team XBMC 3 | * http://www.xbmc.org 4 | * Copyright (C) 2008-2009 Andrej Stepanchuk 5 | * Copyright (C) 2009-2010 Howard Chu 6 | * 7 | * This file is part of librtmp. 8 | * 9 | * librtmp is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation; either version 2.1, 12 | * or (at your option) any later version. 13 | * 14 | * librtmp is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public License 20 | * along with librtmp see the file COPYING. If not, write to 21 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | * Boston, MA 02110-1301, USA. 23 | * http://www.gnu.org/copyleft/lgpl.html 24 | */ 25 | 26 | #ifndef __BYTES_H__ 27 | #define __BYTES_H__ 28 | 29 | #include 30 | 31 | #ifdef _WIN32 32 | /* Windows is little endian only */ 33 | #define __LITTLE_ENDIAN 1234 34 | #define __BIG_ENDIAN 4321 35 | #define __BYTE_ORDER __LITTLE_ENDIAN 36 | #define __FLOAT_WORD_ORDER __BYTE_ORDER 37 | 38 | typedef unsigned char uint8_t; 39 | 40 | #else /* !_WIN32 */ 41 | 42 | #include 43 | 44 | #if defined(BYTE_ORDER) && !defined(__BYTE_ORDER) 45 | #define __BYTE_ORDER BYTE_ORDER 46 | #endif 47 | 48 | #if defined(BIG_ENDIAN) && !defined(__BIG_ENDIAN) 49 | #define __BIG_ENDIAN BIG_ENDIAN 50 | #endif 51 | 52 | #if defined(LITTLE_ENDIAN) && !defined(__LITTLE_ENDIAN) 53 | #define __LITTLE_ENDIAN LITTLE_ENDIAN 54 | #endif 55 | 56 | #endif /* !_WIN32 */ 57 | 58 | /* define default endianness */ 59 | #ifndef __LITTLE_ENDIAN 60 | #define __LITTLE_ENDIAN 1234 61 | #endif 62 | 63 | #ifndef __BIG_ENDIAN 64 | #define __BIG_ENDIAN 4321 65 | #endif 66 | 67 | #ifndef __BYTE_ORDER 68 | #warning "Byte order not defined on your system, assuming little endian!" 69 | #define __BYTE_ORDER __LITTLE_ENDIAN 70 | #endif 71 | 72 | /* ok, we assume to have the same float word order and byte order if float word order is not defined */ 73 | #ifndef __FLOAT_WORD_ORDER 74 | #warning "Float word order not defined, assuming the same as byte order!" 75 | #define __FLOAT_WORD_ORDER __BYTE_ORDER 76 | #endif 77 | 78 | #if !defined(__BYTE_ORDER) || !defined(__FLOAT_WORD_ORDER) 79 | #error "Undefined byte or float word order!" 80 | #endif 81 | 82 | #if __FLOAT_WORD_ORDER != __BIG_ENDIAN && __FLOAT_WORD_ORDER != __LITTLE_ENDIAN 83 | #error "Unknown/unsupported float word order!" 84 | #endif 85 | 86 | #if __BYTE_ORDER != __BIG_ENDIAN && __BYTE_ORDER != __LITTLE_ENDIAN 87 | #error "Unknown/unsupported byte order!" 88 | #endif 89 | 90 | #endif 91 | 92 | -------------------------------------------------------------------------------- /librtmp/dh.h: -------------------------------------------------------------------------------- 1 | /* RTMPDump - Diffie-Hellmann Key Exchange 2 | * Copyright (C) 2009 Andrej Stepanchuk 3 | * Copyright (C) 2009-2010 Howard Chu 4 | * 5 | * This file is part of librtmp. 6 | * 7 | * librtmp is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU Lesser General Public License as 9 | * published by the Free Software Foundation; either version 2.1, 10 | * or (at your option) any later version. 11 | * 12 | * librtmp is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with librtmp see the file COPYING. If not, write to 19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 | * Boston, MA 02110-1301, USA. 21 | * http://www.gnu.org/copyleft/lgpl.html 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #ifdef USE_POLARSSL 31 | #include 32 | typedef mpi * MP_t; 33 | #define MP_new(m) m = malloc(sizeof(mpi)); mpi_init(m) 34 | #define MP_set_w(mpi, w) mpi_lset(mpi, w) 35 | #define MP_cmp(u, v) mpi_cmp_mpi(u, v) 36 | #define MP_set(u, v) mpi_copy(u, v) 37 | #define MP_sub_w(mpi, w) mpi_sub_int(mpi, mpi, w) 38 | #define MP_cmp_1(mpi) mpi_cmp_int(mpi, 1) 39 | #define MP_modexp(r, y, q, p) mpi_exp_mod(r, y, q, p, NULL) 40 | #define MP_free(mpi) mpi_free(mpi); free(mpi) 41 | #define MP_gethex(u, hex, res) MP_new(u); res = mpi_read_string(u, 16, hex) == 0 42 | #define MP_bytes(u) mpi_size(u) 43 | #define MP_setbin(u,buf,len) mpi_write_binary(u,buf,len) 44 | #define MP_getbin(u,buf,len) MP_new(u); mpi_read_binary(u,buf,len) 45 | 46 | typedef struct MDH { 47 | MP_t p; 48 | MP_t g; 49 | MP_t pub_key; 50 | MP_t priv_key; 51 | long length; 52 | dhm_context ctx; 53 | } MDH; 54 | 55 | #define MDH_new() calloc(1,sizeof(MDH)) 56 | #define MDH_free(vp) {MDH *_dh = vp; dhm_free(&_dh->ctx); MP_free(_dh->p); MP_free(_dh->g); MP_free(_dh->pub_key); MP_free(_dh->priv_key); free(_dh);} 57 | 58 | static int MDH_generate_key(MDH *dh) 59 | { 60 | unsigned char out[2]; 61 | MP_set(&dh->ctx.P, dh->p); 62 | MP_set(&dh->ctx.G, dh->g); 63 | dh->ctx.len = 128; 64 | dhm_make_public(&dh->ctx, 1024, out, 1, havege_rand, &RTMP_TLS_ctx->hs); 65 | MP_new(dh->pub_key); 66 | MP_new(dh->priv_key); 67 | MP_set(dh->pub_key, &dh->ctx.GX); 68 | MP_set(dh->priv_key, &dh->ctx.X); 69 | return 1; 70 | } 71 | 72 | static int MDH_compute_key(uint8_t *secret, size_t len, MP_t pub, MDH *dh) 73 | { 74 | MP_set(&dh->ctx.GY, pub); 75 | dhm_calc_secret(&dh->ctx, secret, &len); 76 | return 0; 77 | } 78 | 79 | #elif defined(USE_GNUTLS) 80 | #include 81 | typedef gcry_mpi_t MP_t; 82 | #define MP_new(m) m = gcry_mpi_new(1) 83 | #define MP_set_w(mpi, w) gcry_mpi_set_ui(mpi, w) 84 | #define MP_cmp(u, v) gcry_mpi_cmp(u, v) 85 | #define MP_set(u, v) gcry_mpi_set(u, v) 86 | #define MP_sub_w(mpi, w) gcry_mpi_sub_ui(mpi, mpi, w) 87 | #define MP_cmp_1(mpi) gcry_mpi_cmp_ui(mpi, 1) 88 | #define MP_modexp(r, y, q, p) gcry_mpi_powm(r, y, q, p) 89 | #define MP_free(mpi) gcry_mpi_release(mpi) 90 | #define MP_gethex(u, hex, res) res = (gcry_mpi_scan(&u, GCRYMPI_FMT_HEX, hex, 0, 0) == 0) 91 | #define MP_bytes(u) (gcry_mpi_get_nbits(u) + 7) / 8 92 | #define MP_setbin(u,buf,len) gcry_mpi_print(GCRYMPI_FMT_USG,buf,len,NULL,u) 93 | #define MP_getbin(u,buf,len) gcry_mpi_scan(&u,GCRYMPI_FMT_USG,buf,len,NULL) 94 | 95 | typedef struct MDH { 96 | MP_t p; 97 | MP_t g; 98 | MP_t pub_key; 99 | MP_t priv_key; 100 | long length; 101 | } MDH; 102 | 103 | #define MDH_new() calloc(1,sizeof(MDH)) 104 | #define MDH_free(dh) do {MP_free(((MDH*)(dh))->p); MP_free(((MDH*)(dh))->g); MP_free(((MDH*)(dh))->pub_key); MP_free(((MDH*)(dh))->priv_key); free(dh);} while(0) 105 | 106 | extern MP_t gnutls_calc_dh_secret(MP_t *priv, MP_t g, MP_t p); 107 | extern MP_t gnutls_calc_dh_key(MP_t y, MP_t x, MP_t p); 108 | 109 | #define MDH_generate_key(dh) (dh->pub_key = gnutls_calc_dh_secret(&dh->priv_key, dh->g, dh->p)) 110 | static int MDH_compute_key(uint8_t *secret, size_t len, MP_t pub, MDH *dh) 111 | { 112 | MP_t sec = gnutls_calc_dh_key(pub, dh->priv_key, dh->p); 113 | if (sec) 114 | { 115 | MP_setbin(sec, secret, len); 116 | MP_free(sec); 117 | return 0; 118 | } 119 | else 120 | return -1; 121 | } 122 | 123 | #else /* USE_OPENSSL */ 124 | #include 125 | #include 126 | 127 | typedef BIGNUM * MP_t; 128 | #define MP_new(m) m = BN_new() 129 | #define MP_set_w(mpi, w) BN_set_word(mpi, w) 130 | #define MP_cmp(u, v) BN_cmp(u, v) 131 | #define MP_set(u, v) BN_copy(u, v) 132 | #define MP_sub_w(mpi, w) BN_sub_word(mpi, w) 133 | #define MP_cmp_1(mpi) BN_cmp(mpi, BN_value_one()) 134 | #define MP_modexp(r, y, q, p) do {BN_CTX *ctx = BN_CTX_new(); BN_mod_exp(r, y, q, p, ctx); BN_CTX_free(ctx);} while(0) 135 | #define MP_free(mpi) BN_free(mpi) 136 | #define MP_gethex(u, hex, res) res = BN_hex2bn(&u, hex) 137 | #define MP_bytes(u) BN_num_bytes(u) 138 | #define MP_setbin(u,buf,len) BN_bn2bin(u,buf) 139 | #define MP_getbin(u,buf,len) u = BN_bin2bn(buf,len,0) 140 | 141 | #define MDH DH 142 | #define MDH_new() DH_new() 143 | #define MDH_free(dh) DH_free(dh) 144 | #define MDH_generate_key(dh) DH_generate_key(dh) 145 | #define MDH_compute_key(secret, seclen, pub, dh) DH_compute_key(secret, pub, dh) 146 | 147 | #endif 148 | 149 | #include "log.h" 150 | #include "dhgroups.h" 151 | 152 | /* RFC 2631, Section 2.1.5, http://www.ietf.org/rfc/rfc2631.txt */ 153 | static int 154 | isValidPublicKey(MP_t y, MP_t p, MP_t q) 155 | { 156 | int ret = TRUE; 157 | MP_t bn; 158 | assert(y); 159 | 160 | MP_new(bn); 161 | assert(bn); 162 | 163 | /* y must lie in [2,p-1] */ 164 | MP_set_w(bn, 1); 165 | if (MP_cmp(y, bn) < 0) 166 | { 167 | RTMP_Log(RTMP_LOGERROR, "DH public key must be at least 2"); 168 | ret = FALSE; 169 | goto failed; 170 | } 171 | 172 | /* bn = p-2 */ 173 | MP_set(bn, p); 174 | MP_sub_w(bn, 1); 175 | if (MP_cmp(y, bn) > 0) 176 | { 177 | RTMP_Log(RTMP_LOGERROR, "DH public key must be at most p-2"); 178 | ret = FALSE; 179 | goto failed; 180 | } 181 | 182 | /* Verify with Sophie-Germain prime 183 | * 184 | * This is a nice test to make sure the public key position is calculated 185 | * correctly. This test will fail in about 50% of the cases if applied to 186 | * random data. 187 | */ 188 | if (q) 189 | { 190 | /* y must fulfill y^q mod p = 1 */ 191 | MP_modexp(bn, y, q, p); 192 | 193 | if (MP_cmp_1(bn) != 0) 194 | { 195 | RTMP_Log(RTMP_LOGWARNING, "DH public key does not fulfill y^q mod p = 1"); 196 | } 197 | } 198 | 199 | failed: 200 | MP_free(bn); 201 | return ret; 202 | } 203 | 204 | static MDH * 205 | DHInit(int nKeyBits) 206 | { 207 | size_t res; 208 | MDH *dh = MDH_new(); 209 | 210 | if (!dh) 211 | goto failed; 212 | 213 | MP_new(dh->g); 214 | 215 | if (!dh->g) 216 | goto failed; 217 | 218 | MP_gethex(dh->p, P1024, res); /* prime P1024, see dhgroups.h */ 219 | if (!res) 220 | { 221 | goto failed; 222 | } 223 | 224 | MP_set_w(dh->g, 2); /* base 2 */ 225 | 226 | dh->length = nKeyBits; 227 | return dh; 228 | 229 | failed: 230 | if (dh) 231 | MDH_free(dh); 232 | 233 | return 0; 234 | } 235 | 236 | static int 237 | DHGenerateKey(MDH *dh) 238 | { 239 | size_t res = 0; 240 | if (!dh) 241 | return 0; 242 | 243 | while (!res) 244 | { 245 | MP_t q1 = NULL; 246 | 247 | if (!MDH_generate_key(dh)) 248 | return 0; 249 | 250 | MP_gethex(q1, Q1024, res); 251 | assert(res); 252 | 253 | res = isValidPublicKey(dh->pub_key, dh->p, q1); 254 | if (!res) 255 | { 256 | MP_free(dh->pub_key); 257 | MP_free(dh->priv_key); 258 | dh->pub_key = dh->priv_key = 0; 259 | } 260 | 261 | MP_free(q1); 262 | } 263 | return 1; 264 | } 265 | 266 | /* fill pubkey with the public key in BIG ENDIAN order 267 | * 00 00 00 00 00 x1 x2 x3 ..... 268 | */ 269 | 270 | static int 271 | DHGetPublicKey(MDH *dh, uint8_t *pubkey, size_t nPubkeyLen) 272 | { 273 | int len; 274 | if (!dh || !dh->pub_key) 275 | return 0; 276 | 277 | len = MP_bytes(dh->pub_key); 278 | if (len <= 0 || len > (int) nPubkeyLen) 279 | return 0; 280 | 281 | memset(pubkey, 0, nPubkeyLen); 282 | MP_setbin(dh->pub_key, pubkey + (nPubkeyLen - len), len); 283 | return 1; 284 | } 285 | 286 | #if 0 /* unused */ 287 | static int 288 | DHGetPrivateKey(MDH *dh, uint8_t *privkey, size_t nPrivkeyLen) 289 | { 290 | if (!dh || !dh->priv_key) 291 | return 0; 292 | 293 | int len = MP_bytes(dh->priv_key); 294 | if (len <= 0 || len > (int) nPrivkeyLen) 295 | return 0; 296 | 297 | memset(privkey, 0, nPrivkeyLen); 298 | MP_setbin(dh->priv_key, privkey + (nPrivkeyLen - len), len); 299 | return 1; 300 | } 301 | #endif 302 | 303 | /* computes the shared secret key from the private MDH value and the 304 | * other party's public key (pubkey) 305 | */ 306 | static int 307 | DHComputeSharedSecretKey(MDH *dh, uint8_t *pubkey, size_t nPubkeyLen, 308 | uint8_t *secret) 309 | { 310 | MP_t q1 = NULL, pubkeyBn = NULL; 311 | size_t len; 312 | int res; 313 | 314 | if (!dh || !secret || nPubkeyLen >= INT_MAX) 315 | return -1; 316 | 317 | MP_getbin(pubkeyBn, pubkey, nPubkeyLen); 318 | if (!pubkeyBn) 319 | return -1; 320 | 321 | MP_gethex(q1, Q1024, len); 322 | assert(len); 323 | 324 | if (isValidPublicKey(pubkeyBn, dh->p, q1)) 325 | res = MDH_compute_key(secret, nPubkeyLen, pubkeyBn, dh); 326 | else 327 | res = -1; 328 | 329 | MP_free(q1); 330 | MP_free(pubkeyBn); 331 | 332 | return res; 333 | } 334 | -------------------------------------------------------------------------------- /librtmp/dhgroups.h: -------------------------------------------------------------------------------- 1 | /* librtmp - Diffie-Hellmann Key Exchange 2 | * Copyright (C) 2009 Andrej Stepanchuk 3 | * 4 | * This file is part of librtmp. 5 | * 6 | * librtmp is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as 8 | * published by the Free Software Foundation; either version 2.1, 9 | * or (at your option) any later version. 10 | * 11 | * librtmp is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with librtmp see the file COPYING. If not, write to 18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 | * Boston, MA 02110-1301, USA. 20 | * http://www.gnu.org/copyleft/lgpl.html 21 | */ 22 | 23 | /* from RFC 3526, see http://www.ietf.org/rfc/rfc3526.txt */ 24 | 25 | /* 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 } */ 26 | #define P768 \ 27 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ 28 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ 29 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ 30 | "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF" 31 | 32 | /* 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 } */ 33 | #define P1024 \ 34 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ 35 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ 36 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ 37 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ 38 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \ 39 | "FFFFFFFFFFFFFFFF" 40 | 41 | /* Group morder largest prime factor: */ 42 | #define Q1024 \ 43 | "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68" \ 44 | "948127044533E63A0105DF531D89CD9128A5043CC71A026E" \ 45 | "F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122" \ 46 | "F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6" \ 47 | "F71C35FDAD44CFD2D74F9208BE258FF324943328F67329C0" \ 48 | "FFFFFFFFFFFFFFFF" 49 | 50 | /* 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 } */ 51 | #define P1536 \ 52 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ 53 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ 54 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ 55 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ 56 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ 57 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ 58 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ 59 | "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF" 60 | 61 | /* 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } */ 62 | #define P2048 \ 63 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ 64 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ 65 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ 66 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ 67 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ 68 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ 69 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ 70 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ 71 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ 72 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ 73 | "15728E5A8AACAA68FFFFFFFFFFFFFFFF" 74 | 75 | /* 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 } */ 76 | #define P3072 \ 77 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ 78 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ 79 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ 80 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ 81 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ 82 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ 83 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ 84 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ 85 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ 86 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ 87 | "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ 88 | "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ 89 | "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ 90 | "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ 91 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ 92 | "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF" 93 | 94 | /* 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 } */ 95 | #define P4096 \ 96 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ 97 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ 98 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ 99 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ 100 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ 101 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ 102 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ 103 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ 104 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ 105 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ 106 | "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ 107 | "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ 108 | "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ 109 | "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ 110 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ 111 | "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ 112 | "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ 113 | "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ 114 | "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ 115 | "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ 116 | "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" \ 117 | "FFFFFFFFFFFFFFFF" 118 | 119 | /* 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 } */ 120 | #define P6144 \ 121 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ 122 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ 123 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ 124 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ 125 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ 126 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ 127 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ 128 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ 129 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ 130 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ 131 | "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ 132 | "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ 133 | "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ 134 | "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ 135 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ 136 | "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ 137 | "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ 138 | "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ 139 | "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ 140 | "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ 141 | "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \ 142 | "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \ 143 | "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \ 144 | "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \ 145 | "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \ 146 | "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \ 147 | "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \ 148 | "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \ 149 | "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \ 150 | "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \ 151 | "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \ 152 | "12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF" 153 | 154 | /* 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 } */ 155 | #define P8192 \ 156 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ 157 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ 158 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ 159 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ 160 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ 161 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ 162 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ 163 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ 164 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ 165 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ 166 | "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ 167 | "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ 168 | "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ 169 | "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ 170 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ 171 | "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ 172 | "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ 173 | "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ 174 | "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ 175 | "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ 176 | "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \ 177 | "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \ 178 | "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \ 179 | "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \ 180 | "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \ 181 | "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \ 182 | "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \ 183 | "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \ 184 | "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \ 185 | "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \ 186 | "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \ 187 | "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" \ 188 | "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" \ 189 | "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" \ 190 | "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" \ 191 | "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" \ 192 | "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" \ 193 | "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" \ 194 | "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" \ 195 | "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" \ 196 | "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" \ 197 | "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" \ 198 | "60C980DD98EDD3DFFFFFFFFFFFFFFFFF" 199 | 200 | -------------------------------------------------------------------------------- /librtmp/handshake.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008-2009 Andrej Stepanchuk 3 | * Copyright (C) 2009-2010 Howard Chu 4 | * Copyright (C) 2010 2a665470ced7adb7156fcef47f8199a6371c117b8a79e399a2771e0b36384090 5 | * 6 | * This file is part of librtmp. 7 | * 8 | * librtmp is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU Lesser General Public License as 10 | * published by the Free Software Foundation; either version 2.1, 11 | * or (at your option) any later version. 12 | * 13 | * librtmp is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public License 19 | * along with librtmp see the file COPYING. If not, write to 20 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 | * Boston, MA 02110-1301, USA. 22 | * http://www.gnu.org/copyleft/lgpl.html 23 | */ 24 | 25 | /* This file is #included in rtmp.c, it is not meant to be compiled alone */ 26 | 27 | #ifdef USE_POLARSSL 28 | #include 29 | #include 30 | #ifndef SHA256_DIGEST_LENGTH 31 | #define SHA256_DIGEST_LENGTH 32 32 | #endif 33 | #define HMAC_CTX sha2_context 34 | #define HMAC_setup(ctx, key, len) sha2_hmac_starts(&ctx, (unsigned char *)key, len, 0) 35 | #define HMAC_crunch(ctx, buf, len) sha2_hmac_update(&ctx, buf, len) 36 | #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; sha2_hmac_finish(&ctx, dig) 37 | 38 | typedef arc4_context * RC4_handle; 39 | #define RC4_alloc(h) *h = malloc(sizeof(arc4_context)) 40 | #define RC4_setkey(h,l,k) arc4_setup(h,k,l) 41 | #define RC4_encrypt(h,l,d) arc4_crypt(h,l,(unsigned char *)d,(unsigned char *)d) 42 | #define RC4_encrypt2(h,l,s,d) arc4_crypt(h,l,(unsigned char *)s,(unsigned char *)d) 43 | #define RC4_free(h) free(h) 44 | 45 | #elif defined(USE_GNUTLS) 46 | #include 47 | #ifndef SHA256_DIGEST_LENGTH 48 | #define SHA256_DIGEST_LENGTH 32 49 | #endif 50 | #define HMAC_CTX gcry_md_hd_t 51 | #define HMAC_setup(ctx, key, len) gcry_md_open(&ctx, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); gcry_md_setkey(ctx, key, len) 52 | #define HMAC_crunch(ctx, buf, len) gcry_md_write(ctx, buf, len) 53 | #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; memcpy(dig, gcry_md_read(ctx, 0), dlen); gcry_md_close(ctx) 54 | 55 | typedef gcry_cipher_hd_t RC4_handle; 56 | #define RC4_alloc(h) gcry_cipher_open(h, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0) 57 | #define RC4_setkey(h,l,k) gcry_cipher_setkey(h,k,l) 58 | #define RC4_encrypt(h,l,d) gcry_cipher_encrypt(h,(void *)d,l,NULL,0) 59 | #define RC4_encrypt2(h,l,s,d) gcry_cipher_encrypt(h,(void *)d,l,(void *)s,l) 60 | #define RC4_free(h) gcry_cipher_close(h) 61 | 62 | #else /* USE_OPENSSL */ 63 | #include 64 | #include 65 | #include 66 | #if OPENSSL_VERSION_NUMBER < 0x0090800 || !defined(SHA256_DIGEST_LENGTH) 67 | #error Your OpenSSL is too old, need 0.9.8 or newer with SHA256 68 | #endif 69 | #define HMAC_setup(ctx, key, len) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, key, len, EVP_sha256(), 0) 70 | #define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, buf, len) 71 | #define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, dig, &dlen); HMAC_CTX_cleanup(&ctx) 72 | 73 | typedef RC4_KEY * RC4_handle; 74 | #define RC4_alloc(h) *h = malloc(sizeof(RC4_KEY)) 75 | #define RC4_setkey(h,l,k) RC4_set_key(h,l,k) 76 | #define RC4_encrypt(h,l,d) RC4(h,l,(uint8_t *)d,(uint8_t *)d) 77 | #define RC4_encrypt2(h,l,s,d) RC4(h,l,(uint8_t *)s,(uint8_t *)d) 78 | #define RC4_free(h) free(h) 79 | #endif 80 | 81 | #define FP10 82 | 83 | #include "dh.h" 84 | 85 | static const uint8_t GenuineFMSKey[] = { 86 | 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 87 | 0x65, 0x20, 0x46, 0x6c, 88 | 0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x20, 0x53, 0x65, 89 | 0x72, 0x76, 0x65, 0x72, 90 | 0x20, 0x30, 0x30, 0x31, /* Genuine Adobe Flash Media Server 001 */ 91 | 92 | 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1, 93 | 0x02, 0x9e, 0x7e, 0x57, 0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 94 | 0x93, 0xb8, 0xe6, 0x36, 95 | 0xcf, 0xeb, 0x31, 0xae 96 | }; /* 68 */ 97 | 98 | static const uint8_t GenuineFPKey[] = { 99 | 0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20, 0x41, 0x64, 0x6F, 0x62, 100 | 0x65, 0x20, 0x46, 0x6C, 101 | 0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x20, 0x30, 102 | 0x30, 0x31, /* Genuine Adobe Flash Player 001 */ 103 | 0xF0, 0xEE, 104 | 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02, 0x9E, 105 | 0x7E, 0x57, 0x6E, 0xEC, 106 | 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 107 | 0x31, 0xAE 108 | }; /* 62 */ 109 | 110 | static void InitRC4Encryption 111 | (uint8_t * secretKey, 112 | uint8_t * pubKeyIn, 113 | uint8_t * pubKeyOut, RC4_handle *rc4keyIn, RC4_handle *rc4keyOut) 114 | { 115 | uint8_t digest[SHA256_DIGEST_LENGTH]; 116 | unsigned int digestLen = 0; 117 | HMAC_CTX ctx; 118 | 119 | RC4_alloc(rc4keyIn); 120 | RC4_alloc(rc4keyOut); 121 | 122 | HMAC_setup(ctx, secretKey, 128); 123 | HMAC_crunch(ctx, pubKeyIn, 128); 124 | HMAC_finish(ctx, digest, digestLen); 125 | 126 | RTMP_Log(RTMP_LOGDEBUG, "RC4 Out Key: "); 127 | RTMP_LogHex(RTMP_LOGDEBUG, digest, 16); 128 | 129 | RC4_setkey(*rc4keyOut, 16, digest); 130 | 131 | HMAC_setup(ctx, secretKey, 128); 132 | HMAC_crunch(ctx, pubKeyOut, 128); 133 | HMAC_finish(ctx, digest, digestLen); 134 | 135 | RTMP_Log(RTMP_LOGDEBUG, "RC4 In Key: "); 136 | RTMP_LogHex(RTMP_LOGDEBUG, digest, 16); 137 | 138 | RC4_setkey(*rc4keyIn, 16, digest); 139 | } 140 | 141 | typedef unsigned int (getoff)(uint8_t *buf, unsigned int len); 142 | 143 | static unsigned int 144 | GetDHOffset2(uint8_t *handshake, unsigned int len) 145 | { 146 | unsigned int offset = 0; 147 | uint8_t *ptr = handshake + 768; 148 | unsigned int res; 149 | 150 | assert(RTMP_SIG_SIZE <= len); 151 | 152 | offset += (*ptr); 153 | ptr++; 154 | offset += (*ptr); 155 | ptr++; 156 | offset += (*ptr); 157 | ptr++; 158 | offset += (*ptr); 159 | 160 | res = (offset % 632) + 8; 161 | 162 | if (res + 128 > 767) 163 | { 164 | RTMP_Log(RTMP_LOGERROR, 165 | "%s: Couldn't calculate correct DH offset (got %d), exiting!", 166 | __FUNCTION__, res); 167 | exit(1); 168 | } 169 | return res; 170 | } 171 | 172 | static unsigned int 173 | GetDigestOffset2(uint8_t *handshake, unsigned int len) 174 | { 175 | unsigned int offset = 0; 176 | uint8_t *ptr = handshake + 772; 177 | unsigned int res; 178 | 179 | offset += (*ptr); 180 | ptr++; 181 | offset += (*ptr); 182 | ptr++; 183 | offset += (*ptr); 184 | ptr++; 185 | offset += (*ptr); 186 | 187 | res = (offset % 728) + 776; 188 | 189 | if (res + 32 > 1535) 190 | { 191 | RTMP_Log(RTMP_LOGERROR, 192 | "%s: Couldn't calculate correct digest offset (got %d), exiting", 193 | __FUNCTION__, res); 194 | exit(1); 195 | } 196 | return res; 197 | } 198 | 199 | static unsigned int 200 | GetDHOffset1(uint8_t *handshake, unsigned int len) 201 | { 202 | unsigned int offset = 0; 203 | uint8_t *ptr = handshake + 1532; 204 | unsigned int res; 205 | 206 | assert(RTMP_SIG_SIZE <= len); 207 | 208 | offset += (*ptr); 209 | ptr++; 210 | offset += (*ptr); 211 | ptr++; 212 | offset += (*ptr); 213 | ptr++; 214 | offset += (*ptr); 215 | 216 | res = (offset % 632) + 772; 217 | 218 | if (res + 128 > 1531) 219 | { 220 | RTMP_Log(RTMP_LOGERROR, "%s: Couldn't calculate DH offset (got %d), exiting!", 221 | __FUNCTION__, res); 222 | exit(1); 223 | } 224 | 225 | return res; 226 | } 227 | 228 | static unsigned int 229 | GetDigestOffset1(uint8_t *handshake, unsigned int len) 230 | { 231 | unsigned int offset = 0; 232 | uint8_t *ptr = handshake + 8; 233 | unsigned int res; 234 | 235 | assert(12 <= len); 236 | 237 | offset += (*ptr); 238 | ptr++; 239 | offset += (*ptr); 240 | ptr++; 241 | offset += (*ptr); 242 | ptr++; 243 | offset += (*ptr); 244 | 245 | res = (offset % 728) + 12; 246 | 247 | if (res + 32 > 771) 248 | { 249 | RTMP_Log(RTMP_LOGERROR, 250 | "%s: Couldn't calculate digest offset (got %d), exiting!", 251 | __FUNCTION__, res); 252 | exit(1); 253 | } 254 | 255 | return res; 256 | } 257 | 258 | static getoff *digoff[] = {GetDigestOffset1, GetDigestOffset2}; 259 | static getoff *dhoff[] = {GetDHOffset1, GetDHOffset2}; 260 | 261 | static void 262 | HMACsha256(const uint8_t *message, size_t messageLen, const uint8_t *key, 263 | size_t keylen, uint8_t *digest) 264 | { 265 | unsigned int digestLen; 266 | HMAC_CTX ctx; 267 | 268 | HMAC_setup(ctx, key, keylen); 269 | HMAC_crunch(ctx, message, messageLen); 270 | HMAC_finish(ctx, digest, digestLen); 271 | 272 | assert(digestLen == 32); 273 | } 274 | 275 | static void 276 | CalculateDigest(unsigned int digestPos, uint8_t *handshakeMessage, 277 | const uint8_t *key, size_t keyLen, uint8_t *digest) 278 | { 279 | const int messageLen = RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH; 280 | uint8_t message[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH]; 281 | 282 | memcpy(message, handshakeMessage, digestPos); 283 | memcpy(message + digestPos, 284 | &handshakeMessage[digestPos + SHA256_DIGEST_LENGTH], 285 | messageLen - digestPos); 286 | 287 | HMACsha256(message, messageLen, key, keyLen, digest); 288 | } 289 | 290 | static int 291 | VerifyDigest(unsigned int digestPos, uint8_t *handshakeMessage, const uint8_t *key, 292 | size_t keyLen) 293 | { 294 | uint8_t calcDigest[SHA256_DIGEST_LENGTH]; 295 | 296 | CalculateDigest(digestPos, handshakeMessage, key, keyLen, calcDigest); 297 | 298 | return memcmp(&handshakeMessage[digestPos], calcDigest, 299 | SHA256_DIGEST_LENGTH) == 0; 300 | } 301 | 302 | /* handshake 303 | * 304 | * Type = [1 bytes] plain: 0x03, encrypted: 0x06, 0x08, 0x09 305 | * -------------------------------------------------------------------- [1536 bytes] 306 | * Uptime = [4 bytes] big endian unsigned number, uptime 307 | * Version = [4 bytes] each byte represents a version number, e.g. 9.0.124.0 308 | * ... 309 | * 310 | */ 311 | 312 | static const uint32_t rtmpe8_keys[16][4] = { 313 | {0xbff034b2, 0x11d9081f, 0xccdfb795, 0x748de732}, 314 | {0x086a5eb6, 0x1743090e, 0x6ef05ab8, 0xfe5a39e2}, 315 | {0x7b10956f, 0x76ce0521, 0x2388a73a, 0x440149a1}, 316 | {0xa943f317, 0xebf11bb2, 0xa691a5ee, 0x17f36339}, 317 | {0x7a30e00a, 0xb529e22c, 0xa087aea5, 0xc0cb79ac}, 318 | {0xbdce0c23, 0x2febdeff, 0x1cfaae16, 0x1123239d}, 319 | {0x55dd3f7b, 0x77e7e62e, 0x9bb8c499, 0xc9481ee4}, 320 | {0x407bb6b4, 0x71e89136, 0xa7aebf55, 0xca33b839}, 321 | {0xfcf6bdc3, 0xb63c3697, 0x7ce4f825, 0x04d959b2}, 322 | {0x28e091fd, 0x41954c4c, 0x7fb7db00, 0xe3a066f8}, 323 | {0x57845b76, 0x4f251b03, 0x46d45bcd, 0xa2c30d29}, 324 | {0x0acceef8, 0xda55b546, 0x03473452, 0x5863713b}, 325 | {0xb82075dc, 0xa75f1fee, 0xd84268e8, 0xa72a44cc}, 326 | {0x07cf6e9e, 0xa16d7b25, 0x9fa7ae6c, 0xd92f5629}, 327 | {0xfeb1eae4, 0x8c8c3ce1, 0x4e0064a7, 0x6a387c2a}, 328 | {0x893a9427, 0xcc3013a2, 0xf106385b, 0xa829f927} 329 | }; 330 | 331 | /* RTMPE type 8 uses XTEA on the regular signature 332 | * http://en.wikipedia.org/wiki/XTEA 333 | */ 334 | static void rtmpe8_sig(uint8_t *in, uint8_t *out, int keyid) 335 | { 336 | unsigned int i, num_rounds = 32; 337 | uint32_t v0, v1, sum=0, delta=0x9E3779B9; 338 | uint32_t const *k; 339 | 340 | v0 = in[0] | (in[1] << 8) | (in[2] << 16) | (in[3] << 24); 341 | v1 = in[4] | (in[5] << 8) | (in[6] << 16) | (in[7] << 24); 342 | k = rtmpe8_keys[keyid]; 343 | 344 | for (i=0; i < num_rounds; i++) { 345 | v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]); 346 | sum += delta; 347 | v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum>>11) & 3]); 348 | } 349 | 350 | out[0] = v0; v0 >>= 8; 351 | out[1] = v0; v0 >>= 8; 352 | out[2] = v0; v0 >>= 8; 353 | out[3] = v0; 354 | 355 | out[4] = v1; v1 >>= 8; 356 | out[5] = v1; v1 >>= 8; 357 | out[6] = v1; v1 >>= 8; 358 | out[7] = v1; 359 | } 360 | 361 | /* RTMPE type 9 uses Blowfish on the regular signature 362 | * http://en.wikipedia.org/wiki/Blowfish_(cipher) 363 | */ 364 | #define BF_ROUNDS 16 365 | typedef struct bf_key { 366 | uint32_t s[4][256]; 367 | uint32_t p[BF_ROUNDS+2]; 368 | } bf_key; 369 | 370 | static const uint32_t bf_sinit[][256] = { 371 | 372 | /* S-Box 0 */ 373 | { 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 374 | 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 375 | 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, 376 | 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 377 | 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 378 | 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 379 | 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, 380 | 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 381 | 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 382 | 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 383 | 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, 384 | 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, 385 | 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 386 | 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 387 | 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, 388 | 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, 389 | 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 390 | 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 391 | 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 392 | 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 393 | 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 394 | 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 395 | 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 396 | 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 397 | 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 398 | 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 399 | 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, 400 | 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 401 | 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 402 | 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 403 | 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, 404 | 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 405 | 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 406 | 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 407 | 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, 408 | 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 409 | 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 410 | 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 411 | 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, 412 | 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, 413 | 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 414 | 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 415 | 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, }, 416 | 417 | /* S-Box 1 */ 418 | { 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 419 | 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 420 | 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 421 | 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 422 | 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 423 | 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 424 | 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 425 | 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 426 | 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 427 | 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 428 | 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, 429 | 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 430 | 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 431 | 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 432 | 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, 433 | 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 434 | 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 435 | 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 436 | 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 437 | 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, 438 | 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 439 | 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 440 | 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 441 | 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 442 | 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 443 | 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 444 | 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 445 | 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 446 | 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 447 | 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 448 | 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 449 | 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 450 | 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 451 | 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 452 | 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, 453 | 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 454 | 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 455 | 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 456 | 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, 457 | 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 458 | 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 459 | 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 460 | 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, }, 461 | 462 | /* S-Box 2 */ 463 | { 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 464 | 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 465 | 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 466 | 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 467 | 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 468 | 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 469 | 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 470 | 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 471 | 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 472 | 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 473 | 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 474 | 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 475 | 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 476 | 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 477 | 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, 478 | 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 479 | 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 480 | 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 481 | 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, 482 | 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 483 | 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 484 | 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 485 | 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 486 | 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 487 | 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 488 | 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 489 | 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 490 | 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 491 | 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 492 | 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 493 | 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 494 | 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 495 | 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 496 | 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 497 | 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 498 | 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 499 | 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 500 | 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 501 | 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, 502 | 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 503 | 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 504 | 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 505 | 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, }, 506 | 507 | /* S-Box 3 */ 508 | { 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 509 | 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 510 | 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 511 | 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 512 | 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 513 | 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 514 | 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, 515 | 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, 516 | 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 517 | 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 518 | 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 519 | 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 520 | 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 521 | 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 522 | 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, 523 | 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 524 | 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 525 | 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 526 | 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, 527 | 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 528 | 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 529 | 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 530 | 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, 531 | 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 532 | 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 533 | 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 534 | 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, 535 | 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, 536 | 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 537 | 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 538 | 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 539 | 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, 540 | 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 541 | 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 542 | 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, 543 | 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 544 | 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 545 | 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 546 | 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, 547 | 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, 548 | 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 549 | 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 550 | 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, }, 551 | }; 552 | 553 | static const uint32_t bf_pinit[] = { 554 | /* P-Box */ 555 | 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 556 | 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 557 | 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b, 558 | }; 559 | 560 | #define KEYBYTES 24 561 | 562 | static const unsigned char rtmpe9_keys[16][KEYBYTES] = { 563 | { 0x79, 0x34, 0x77, 0x4c, 0x67, 0xd1, 0x38, 0x3a, 0xdf, 0xb3, 0x56, 0xbe, 564 | 0x8b, 0x7b, 0xd0, 0x24, 0x38, 0xe0, 0x73, 0x58, 0x41, 0x5d, 0x69, 0x67, }, 565 | { 0x46, 0xf6, 0xb4, 0xcc, 0x01, 0x93, 0xe3, 0xa1, 0x9e, 0x7d, 0x3c, 0x65, 566 | 0x55, 0x86, 0xfd, 0x09, 0x8f, 0xf7, 0xb3, 0xc4, 0x6f, 0x41, 0xca, 0x5c, }, 567 | { 0x1a, 0xe7, 0xe2, 0xf3, 0xf9, 0x14, 0x79, 0x94, 0xc0, 0xd3, 0x97, 0x43, 568 | 0x08, 0x7b, 0xb3, 0x84, 0x43, 0x2f, 0x9d, 0x84, 0x3f, 0x21, 0x01, 0x9b, }, 569 | { 0xd3, 0xe3, 0x54, 0xb0, 0xf7, 0x1d, 0xf6, 0x2b, 0x5a, 0x43, 0x4d, 0x04, 570 | 0x83, 0x64, 0x3e, 0x0d, 0x59, 0x2f, 0x61, 0xcb, 0xb1, 0x6a, 0x59, 0x0d, }, 571 | { 0xc8, 0xc1, 0xe9, 0xb8, 0x16, 0x56, 0x99, 0x21, 0x7b, 0x5b, 0x36, 0xb7, 572 | 0xb5, 0x9b, 0xdf, 0x06, 0x49, 0x2c, 0x97, 0xf5, 0x95, 0x48, 0x85, 0x7e, }, 573 | { 0xeb, 0xe5, 0xe6, 0x2e, 0xa4, 0xba, 0xd4, 0x2c, 0xf2, 0x16, 0xe0, 0x8f, 574 | 0x66, 0x23, 0xa9, 0x43, 0x41, 0xce, 0x38, 0x14, 0x84, 0x95, 0x00, 0x53, }, 575 | { 0x66, 0xdb, 0x90, 0xf0, 0x3b, 0x4f, 0xf5, 0x6f, 0xe4, 0x9c, 0x20, 0x89, 576 | 0x35, 0x5e, 0xd2, 0xb2, 0xc3, 0x9e, 0x9f, 0x7f, 0x63, 0xb2, 0x28, 0x81, }, 577 | { 0xbb, 0x20, 0xac, 0xed, 0x2a, 0x04, 0x6a, 0x19, 0x94, 0x98, 0x9b, 0xc8, 578 | 0xff, 0xcd, 0x93, 0xef, 0xc6, 0x0d, 0x56, 0xa7, 0xeb, 0x13, 0xd9, 0x30, }, 579 | { 0xbc, 0xf2, 0x43, 0x82, 0x09, 0x40, 0x8a, 0x87, 0x25, 0x43, 0x6d, 0xe6, 580 | 0xbb, 0xa4, 0xb9, 0x44, 0x58, 0x3f, 0x21, 0x7c, 0x99, 0xbb, 0x3f, 0x24, }, 581 | { 0xec, 0x1a, 0xaa, 0xcd, 0xce, 0xbd, 0x53, 0x11, 0xd2, 0xfb, 0x83, 0xb6, 582 | 0xc3, 0xba, 0xab, 0x4f, 0x62, 0x79, 0xe8, 0x65, 0xa9, 0x92, 0x28, 0x76, }, 583 | { 0xc6, 0x0c, 0x30, 0x03, 0x91, 0x18, 0x2d, 0x7b, 0x79, 0xda, 0xe1, 0xd5, 584 | 0x64, 0x77, 0x9a, 0x12, 0xc5, 0xb1, 0xd7, 0x91, 0x4f, 0x96, 0x4c, 0xa3, }, 585 | { 0xd7, 0x7c, 0x2a, 0xbf, 0xa6, 0xe7, 0x85, 0x7c, 0x45, 0xad, 0xff, 0x12, 586 | 0x94, 0xd8, 0xde, 0xa4, 0x5c, 0x3d, 0x79, 0xa4, 0x44, 0x02, 0x5d, 0x22, }, 587 | { 0x16, 0x19, 0x0d, 0x81, 0x6a, 0x4c, 0xc7, 0xf8, 0xb8, 0xf9, 0x4e, 0xcd, 588 | 0x2c, 0x9e, 0x90, 0x84, 0xb2, 0x08, 0x25, 0x60, 0xe1, 0x1e, 0xae, 0x18, }, 589 | { 0xe9, 0x7c, 0x58, 0x26, 0x1b, 0x51, 0x9e, 0x49, 0x82, 0x60, 0x61, 0xfc, 590 | 0xa0, 0xa0, 0x1b, 0xcd, 0xf5, 0x05, 0xd6, 0xa6, 0x6d, 0x07, 0x88, 0xa3, }, 591 | { 0x2b, 0x97, 0x11, 0x8b, 0xd9, 0x4e, 0xd9, 0xdf, 0x20, 0xe3, 0x9c, 0x10, 592 | 0xe6, 0xa1, 0x35, 0x21, 0x11, 0xf9, 0x13, 0x0d, 0x0b, 0x24, 0x65, 0xb2, }, 593 | { 0x53, 0x6a, 0x4c, 0x54, 0xac, 0x8b, 0x9b, 0xb8, 0x97, 0x29, 0xfc, 0x60, 594 | 0x2c, 0x5b, 0x3a, 0x85, 0x68, 0xb5, 0xaa, 0x6a, 0x44, 0xcd, 0x3f, 0xa7, }, 595 | }; 596 | 597 | #define BF_ENC(X,S) \ 598 | (((S[0][X>>24] + S[1][X>>16 & 0xff]) ^ S[2][(X>>8) & 0xff]) + S[3][X & 0xff]) 599 | 600 | static void bf_enc(uint32_t *x, bf_key *key) 601 | { 602 | uint32_t Xl; 603 | uint32_t Xr; 604 | uint32_t temp; 605 | int i; 606 | 607 | Xl = x[0]; 608 | Xr = x[1]; 609 | 610 | for (i = 0; i < BF_ROUNDS; ++i) { 611 | Xl ^= key->p[i]; 612 | Xr ^= BF_ENC(Xl,key->s); 613 | 614 | temp = Xl; 615 | Xl = Xr; 616 | Xr = temp; 617 | } 618 | 619 | Xl ^= key->p[BF_ROUNDS]; 620 | Xr ^= key->p[BF_ROUNDS + 1]; 621 | 622 | x[0] = Xr; 623 | x[1] = Xl; 624 | } 625 | 626 | static void bf_setkey(const unsigned char *kp, int keybytes, bf_key *key) 627 | { 628 | int i; 629 | int j; 630 | int k; 631 | uint32_t data; 632 | uint32_t d[2]; 633 | 634 | memcpy(key->p, bf_pinit, sizeof(key->p)); 635 | memcpy(key->s, bf_sinit, sizeof(key->s)); 636 | 637 | j = 0; 638 | for (i = 0; i < BF_ROUNDS + 2; ++i) { 639 | data = 0x00000000; 640 | for (k = 0; k < 4; ++k) { 641 | data = (data << 8) | kp[j]; 642 | j = j + 1; 643 | if (j >= keybytes) { 644 | j = 0; 645 | } 646 | } 647 | key->p[i] ^= data; 648 | } 649 | 650 | d[0] = 0x00000000; 651 | d[1] = 0x00000000; 652 | 653 | for (i = 0; i < BF_ROUNDS + 2; i += 2) { 654 | bf_enc(d, key); 655 | 656 | key->p[i] = d[0]; 657 | key->p[i + 1] = d[1]; 658 | } 659 | 660 | for (i = 0; i < 4; ++i) { 661 | for (j = 0; j < 256; j += 2) { 662 | 663 | bf_enc(d, key); 664 | 665 | key->s[i][j] = d[0]; 666 | key->s[i][j + 1] = d[1]; 667 | } 668 | } 669 | } 670 | 671 | static void rtmpe9_sig(uint8_t *in, uint8_t *out, int keyid) 672 | { 673 | uint32_t d[2]; 674 | bf_key key; 675 | 676 | bf_setkey(rtmpe9_keys[keyid], KEYBYTES, &key); 677 | 678 | /* input is little-endian */ 679 | d[0] = in[0] | (in[1] << 8) | (in[2] << 16) | (in[3] << 24); 680 | d[1] = in[4] | (in[5] << 8) | (in[6] << 16) | (in[7] << 24); 681 | bf_enc(d, &key); 682 | out[0] = d[0] & 0xff; 683 | out[1] = (d[0] >> 8) & 0xff; 684 | out[2] = (d[0] >> 16) & 0xff; 685 | out[3] = (d[0] >> 24) & 0xff; 686 | out[4] = d[1] & 0xff; 687 | out[5] = (d[1] >> 8) & 0xff; 688 | out[6] = (d[1] >> 16) & 0xff; 689 | out[7] = (d[1] >> 24) & 0xff; 690 | } 691 | 692 | static int 693 | HandShake(RTMP * r, int FP9HandShake) 694 | { 695 | int i, offalg = 0; 696 | int dhposClient = 0; 697 | int digestPosClient = 0; 698 | int encrypted = r->Link.protocol & RTMP_FEATURE_ENC; 699 | 700 | RC4_handle keyIn = 0; 701 | RC4_handle keyOut = 0; 702 | 703 | int32_t *ip; 704 | uint32_t uptime; 705 | 706 | uint8_t clientbuf[RTMP_SIG_SIZE + 4], *clientsig=clientbuf+4; 707 | uint8_t serversig[RTMP_SIG_SIZE], client2[RTMP_SIG_SIZE], *reply; 708 | uint8_t type; 709 | getoff *getdh = NULL, *getdig = NULL; 710 | 711 | if (encrypted || r->Link.SWFSize) 712 | FP9HandShake = TRUE; 713 | else 714 | FP9HandShake = FALSE; 715 | 716 | r->Link.rc4keyIn = r->Link.rc4keyOut = 0; 717 | 718 | if (encrypted) 719 | { 720 | clientsig[-1] = 0x06; /* 0x08 is RTMPE as well */ 721 | offalg = 1; 722 | } 723 | else 724 | clientsig[-1] = 0x03; 725 | 726 | uptime = htonl(RTMP_GetTime()); 727 | memcpy(clientsig, &uptime, 4); 728 | 729 | if (FP9HandShake) 730 | { 731 | /* set version to at least 9.0.115.0 */ 732 | if (encrypted) 733 | { 734 | clientsig[4] = 128; 735 | clientsig[6] = 3; 736 | } 737 | else 738 | { 739 | clientsig[4] = 10; 740 | clientsig[6] = 45; 741 | } 742 | clientsig[5] = 0; 743 | clientsig[7] = 2; 744 | 745 | RTMP_Log(RTMP_LOGDEBUG, "%s: Client type: %02X", __FUNCTION__, clientsig[-1]); 746 | getdig = digoff[offalg]; 747 | getdh = dhoff[offalg]; 748 | } 749 | else 750 | { 751 | memset(&clientsig[4], 0, 4); 752 | } 753 | 754 | /* generate random data */ 755 | #ifdef _DEBUG 756 | memset(clientsig+8, 0, RTMP_SIG_SIZE-8); 757 | #else 758 | ip = (int32_t *)(clientsig+8); 759 | for (i = 2; i < RTMP_SIG_SIZE/4; i++) 760 | *ip++ = rand(); 761 | #endif 762 | 763 | /* set handshake digest */ 764 | if (FP9HandShake) 765 | { 766 | if (encrypted) 767 | { 768 | /* generate Diffie-Hellmann parameters */ 769 | r->Link.dh = DHInit(1024); 770 | if (!r->Link.dh) 771 | { 772 | RTMP_Log(RTMP_LOGERROR, "%s: Couldn't initialize Diffie-Hellmann!", 773 | __FUNCTION__); 774 | return FALSE; 775 | } 776 | 777 | dhposClient = getdh(clientsig, RTMP_SIG_SIZE); 778 | RTMP_Log(RTMP_LOGDEBUG, "%s: DH pubkey position: %d", __FUNCTION__, dhposClient); 779 | 780 | if (!DHGenerateKey(r->Link.dh)) 781 | { 782 | RTMP_Log(RTMP_LOGERROR, "%s: Couldn't generate Diffie-Hellmann public key!", 783 | __FUNCTION__); 784 | return FALSE; 785 | } 786 | 787 | if (!DHGetPublicKey(r->Link.dh, &clientsig[dhposClient], 128)) 788 | { 789 | RTMP_Log(RTMP_LOGERROR, "%s: Couldn't write public key!", __FUNCTION__); 790 | return FALSE; 791 | } 792 | } 793 | 794 | digestPosClient = getdig(clientsig, RTMP_SIG_SIZE); /* reuse this value in verification */ 795 | RTMP_Log(RTMP_LOGDEBUG, "%s: Client digest offset: %d", __FUNCTION__, 796 | digestPosClient); 797 | 798 | CalculateDigest(digestPosClient, clientsig, GenuineFPKey, 30, 799 | &clientsig[digestPosClient]); 800 | 801 | RTMP_Log(RTMP_LOGDEBUG, "%s: Initial client digest: ", __FUNCTION__); 802 | RTMP_LogHex(RTMP_LOGDEBUG, clientsig + digestPosClient, 803 | SHA256_DIGEST_LENGTH); 804 | } 805 | 806 | #ifdef _DEBUG 807 | RTMP_Log(RTMP_LOGDEBUG, "Clientsig: "); 808 | RTMP_LogHex(RTMP_LOGDEBUG, clientsig, RTMP_SIG_SIZE); 809 | #endif 810 | 811 | if (!WriteN(r, (char *)clientsig-1, RTMP_SIG_SIZE + 1)) 812 | return FALSE; 813 | 814 | if (ReadN(r, (char *)&type, 1) != 1) /* 0x03 or 0x06 */ 815 | return FALSE; 816 | 817 | RTMP_Log(RTMP_LOGDEBUG, "%s: Type Answer : %02X", __FUNCTION__, type); 818 | 819 | if (type != clientsig[-1]) 820 | RTMP_Log(RTMP_LOGWARNING, "%s: Type mismatch: client sent %d, server answered %d", 821 | __FUNCTION__, clientsig[-1], type); 822 | 823 | if (ReadN(r, (char *)serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE) 824 | return FALSE; 825 | 826 | /* decode server response */ 827 | memcpy(&uptime, serversig, 4); 828 | uptime = ntohl(uptime); 829 | 830 | RTMP_Log(RTMP_LOGDEBUG, "%s: Server Uptime : %d", __FUNCTION__, uptime); 831 | RTMP_Log(RTMP_LOGDEBUG, "%s: FMS Version : %d.%d.%d.%d", __FUNCTION__, serversig[4], 832 | serversig[5], serversig[6], serversig[7]); 833 | 834 | if (FP9HandShake && type == 3 && !serversig[4]) 835 | FP9HandShake = FALSE; 836 | 837 | #ifdef _DEBUG 838 | RTMP_Log(RTMP_LOGDEBUG, "Server signature:"); 839 | RTMP_LogHex(RTMP_LOGDEBUG, serversig, RTMP_SIG_SIZE); 840 | #endif 841 | 842 | if (FP9HandShake) 843 | { 844 | uint8_t digestResp[SHA256_DIGEST_LENGTH]; 845 | uint8_t *signatureResp = NULL; 846 | 847 | /* we have to use this signature now to find the correct algorithms for getting the digest and DH positions */ 848 | int digestPosServer = getdig(serversig, RTMP_SIG_SIZE); 849 | 850 | if (!VerifyDigest(digestPosServer, serversig, GenuineFMSKey, 36)) 851 | { 852 | RTMP_Log(RTMP_LOGWARNING, "Trying different position for server digest!"); 853 | offalg ^= 1; 854 | getdig = digoff[offalg]; 855 | getdh = dhoff[offalg]; 856 | digestPosServer = getdig(serversig, RTMP_SIG_SIZE); 857 | 858 | if (!VerifyDigest(digestPosServer, serversig, GenuineFMSKey, 36)) 859 | { 860 | RTMP_Log(RTMP_LOGERROR, "Couldn't verify the server digest"); /* continuing anyway will probably fail */ 861 | return FALSE; 862 | } 863 | } 864 | 865 | /* generate SWFVerification token (SHA256 HMAC hash of decompressed SWF, key are the last 32 bytes of the server handshake) */ 866 | if (r->Link.SWFSize) 867 | { 868 | const char swfVerify[] = { 0x01, 0x01 }; 869 | char *vend = r->Link.SWFVerificationResponse+sizeof(r->Link.SWFVerificationResponse); 870 | 871 | memcpy(r->Link.SWFVerificationResponse, swfVerify, 2); 872 | AMF_EncodeInt32(&r->Link.SWFVerificationResponse[2], vend, r->Link.SWFSize); 873 | AMF_EncodeInt32(&r->Link.SWFVerificationResponse[6], vend, r->Link.SWFSize); 874 | HMACsha256(r->Link.SWFHash, SHA256_DIGEST_LENGTH, 875 | &serversig[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH], 876 | SHA256_DIGEST_LENGTH, 877 | (uint8_t *)&r->Link.SWFVerificationResponse[10]); 878 | } 879 | 880 | /* do Diffie-Hellmann Key exchange for encrypted RTMP */ 881 | if (encrypted) 882 | { 883 | /* compute secret key */ 884 | uint8_t secretKey[128] = { 0 }; 885 | int len, dhposServer; 886 | 887 | dhposServer = getdh(serversig, RTMP_SIG_SIZE); 888 | RTMP_Log(RTMP_LOGDEBUG, "%s: Server DH public key offset: %d", __FUNCTION__, 889 | dhposServer); 890 | len = DHComputeSharedSecretKey(r->Link.dh, &serversig[dhposServer], 891 | 128, secretKey); 892 | if (len < 0) 893 | { 894 | RTMP_Log(RTMP_LOGDEBUG, "%s: Wrong secret key position!", __FUNCTION__); 895 | return FALSE; 896 | } 897 | 898 | RTMP_Log(RTMP_LOGDEBUG, "%s: Secret key: ", __FUNCTION__); 899 | RTMP_LogHex(RTMP_LOGDEBUG, secretKey, 128); 900 | 901 | InitRC4Encryption(secretKey, 902 | (uint8_t *) & serversig[dhposServer], 903 | (uint8_t *) & clientsig[dhposClient], 904 | &keyIn, &keyOut); 905 | } 906 | 907 | 908 | reply = client2; 909 | #ifdef _DEBUG 910 | memset(reply, 0xff, RTMP_SIG_SIZE); 911 | #else 912 | ip = (int32_t *)reply; 913 | for (i = 0; i < RTMP_SIG_SIZE/4; i++) 914 | *ip++ = rand(); 915 | #endif 916 | /* calculate response now */ 917 | signatureResp = reply+RTMP_SIG_SIZE-SHA256_DIGEST_LENGTH; 918 | 919 | HMACsha256(&serversig[digestPosServer], SHA256_DIGEST_LENGTH, 920 | GenuineFPKey, sizeof(GenuineFPKey), digestResp); 921 | HMACsha256(reply, RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH, digestResp, 922 | SHA256_DIGEST_LENGTH, signatureResp); 923 | 924 | /* some info output */ 925 | RTMP_Log(RTMP_LOGDEBUG, 926 | "%s: Calculated digest key from secure key and server digest: ", 927 | __FUNCTION__); 928 | RTMP_LogHex(RTMP_LOGDEBUG, digestResp, SHA256_DIGEST_LENGTH); 929 | 930 | #ifdef FP10 931 | if (type == 8 ) 932 | { 933 | uint8_t *dptr = digestResp; 934 | uint8_t *sig = signatureResp; 935 | /* encrypt signatureResp */ 936 | for (i=0; iLink.rc4keyIn = keyIn; 1040 | r->Link.rc4keyOut = keyOut; 1041 | 1042 | 1043 | /* update the keystreams */ 1044 | if (r->Link.rc4keyIn) 1045 | { 1046 | RC4_encrypt(r->Link.rc4keyIn, RTMP_SIG_SIZE, (uint8_t *) buff); 1047 | } 1048 | 1049 | if (r->Link.rc4keyOut) 1050 | { 1051 | RC4_encrypt(r->Link.rc4keyOut, RTMP_SIG_SIZE, (uint8_t *) buff); 1052 | } 1053 | } 1054 | } 1055 | else 1056 | { 1057 | if (memcmp(serversig, clientsig, RTMP_SIG_SIZE) != 0) 1058 | { 1059 | RTMP_Log(RTMP_LOGWARNING, "%s: client signature does not match!", 1060 | __FUNCTION__); 1061 | } 1062 | } 1063 | 1064 | RTMP_Log(RTMP_LOGDEBUG, "%s: Handshaking finished....", __FUNCTION__); 1065 | return TRUE; 1066 | } 1067 | 1068 | static int 1069 | SHandShake(RTMP * r) 1070 | { 1071 | int i, offalg = 0; 1072 | int dhposServer = 0; 1073 | int digestPosServer = 0; 1074 | RC4_handle keyIn = 0; 1075 | RC4_handle keyOut = 0; 1076 | int FP9HandShake = FALSE; 1077 | int encrypted; 1078 | int32_t *ip; 1079 | 1080 | uint8_t clientsig[RTMP_SIG_SIZE]; 1081 | uint8_t serverbuf[RTMP_SIG_SIZE + 4], *serversig = serverbuf+4; 1082 | uint8_t type; 1083 | uint32_t uptime; 1084 | getoff *getdh = NULL, *getdig = NULL; 1085 | 1086 | if (ReadN(r, (char *)&type, 1) != 1) /* 0x03 or 0x06 */ 1087 | return FALSE; 1088 | 1089 | if (ReadN(r, (char *)clientsig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE) 1090 | return FALSE; 1091 | 1092 | RTMP_Log(RTMP_LOGDEBUG, "%s: Type Requested : %02X", __FUNCTION__, type); 1093 | RTMP_LogHex(RTMP_LOGDEBUG2, clientsig, RTMP_SIG_SIZE); 1094 | 1095 | if (type == 3) 1096 | { 1097 | encrypted = FALSE; 1098 | } 1099 | else if (type == 6 || type == 8) 1100 | { 1101 | offalg = 1; 1102 | encrypted = TRUE; 1103 | FP9HandShake = TRUE; 1104 | r->Link.protocol |= RTMP_FEATURE_ENC; 1105 | /* use FP10 if client is capable */ 1106 | if (clientsig[4] == 128) 1107 | type = 8; 1108 | } 1109 | else 1110 | { 1111 | RTMP_Log(RTMP_LOGERROR, "%s: Unknown version %02x", 1112 | __FUNCTION__, type); 1113 | return FALSE; 1114 | } 1115 | 1116 | if (!FP9HandShake && clientsig[4]) 1117 | FP9HandShake = TRUE; 1118 | 1119 | serversig[-1] = type; 1120 | 1121 | r->Link.rc4keyIn = r->Link.rc4keyOut = 0; 1122 | 1123 | uptime = htonl(RTMP_GetTime()); 1124 | memcpy(serversig, &uptime, 4); 1125 | 1126 | if (FP9HandShake) 1127 | { 1128 | /* Server version */ 1129 | serversig[4] = 3; 1130 | serversig[5] = 5; 1131 | serversig[6] = 1; 1132 | serversig[7] = 1; 1133 | 1134 | getdig = digoff[offalg]; 1135 | getdh = dhoff[offalg]; 1136 | } 1137 | else 1138 | { 1139 | memset(&serversig[4], 0, 4); 1140 | } 1141 | 1142 | /* generate random data */ 1143 | #ifdef _DEBUG 1144 | memset(serversig+8, 0, RTMP_SIG_SIZE-8); 1145 | #else 1146 | ip = (int32_t *)(serversig+8); 1147 | for (i = 2; i < RTMP_SIG_SIZE/4; i++) 1148 | *ip++ = rand(); 1149 | #endif 1150 | 1151 | /* set handshake digest */ 1152 | if (FP9HandShake) 1153 | { 1154 | if (encrypted) 1155 | { 1156 | /* generate Diffie-Hellmann parameters */ 1157 | r->Link.dh = DHInit(1024); 1158 | if (!r->Link.dh) 1159 | { 1160 | RTMP_Log(RTMP_LOGERROR, "%s: Couldn't initialize Diffie-Hellmann!", 1161 | __FUNCTION__); 1162 | return FALSE; 1163 | } 1164 | 1165 | dhposServer = getdh(serversig, RTMP_SIG_SIZE); 1166 | RTMP_Log(RTMP_LOGDEBUG, "%s: DH pubkey position: %d", __FUNCTION__, dhposServer); 1167 | 1168 | if (!DHGenerateKey(r->Link.dh)) 1169 | { 1170 | RTMP_Log(RTMP_LOGERROR, "%s: Couldn't generate Diffie-Hellmann public key!", 1171 | __FUNCTION__); 1172 | return FALSE; 1173 | } 1174 | 1175 | if (!DHGetPublicKey 1176 | (r->Link.dh, (uint8_t *) &serversig[dhposServer], 128)) 1177 | { 1178 | RTMP_Log(RTMP_LOGERROR, "%s: Couldn't write public key!", __FUNCTION__); 1179 | return FALSE; 1180 | } 1181 | } 1182 | 1183 | digestPosServer = getdig(serversig, RTMP_SIG_SIZE); /* reuse this value in verification */ 1184 | RTMP_Log(RTMP_LOGDEBUG, "%s: Server digest offset: %d", __FUNCTION__, 1185 | digestPosServer); 1186 | 1187 | CalculateDigest(digestPosServer, serversig, GenuineFMSKey, 36, 1188 | &serversig[digestPosServer]); 1189 | 1190 | RTMP_Log(RTMP_LOGDEBUG, "%s: Initial server digest: ", __FUNCTION__); 1191 | RTMP_LogHex(RTMP_LOGDEBUG, serversig + digestPosServer, 1192 | SHA256_DIGEST_LENGTH); 1193 | } 1194 | 1195 | RTMP_Log(RTMP_LOGDEBUG2, "Serversig: "); 1196 | RTMP_LogHex(RTMP_LOGDEBUG2, serversig, RTMP_SIG_SIZE); 1197 | 1198 | if (!WriteN(r, (char *)serversig-1, RTMP_SIG_SIZE + 1)) 1199 | return FALSE; 1200 | 1201 | /* decode client response */ 1202 | memcpy(&uptime, clientsig, 4); 1203 | uptime = ntohl(uptime); 1204 | 1205 | RTMP_Log(RTMP_LOGDEBUG, "%s: Client Uptime : %d", __FUNCTION__, uptime); 1206 | RTMP_Log(RTMP_LOGDEBUG, "%s: Player Version: %d.%d.%d.%d", __FUNCTION__, clientsig[4], 1207 | clientsig[5], clientsig[6], clientsig[7]); 1208 | 1209 | if (FP9HandShake) 1210 | { 1211 | uint8_t digestResp[SHA256_DIGEST_LENGTH]; 1212 | uint8_t *signatureResp = NULL; 1213 | 1214 | /* we have to use this signature now to find the correct algorithms for getting the digest and DH positions */ 1215 | int digestPosClient = getdig(clientsig, RTMP_SIG_SIZE); 1216 | 1217 | if (!VerifyDigest(digestPosClient, clientsig, GenuineFPKey, 30)) 1218 | { 1219 | RTMP_Log(RTMP_LOGWARNING, "Trying different position for client digest!"); 1220 | offalg ^= 1; 1221 | getdig = digoff[offalg]; 1222 | getdh = dhoff[offalg]; 1223 | 1224 | digestPosClient = getdig(clientsig, RTMP_SIG_SIZE); 1225 | 1226 | if (!VerifyDigest(digestPosClient, clientsig, GenuineFPKey, 30)) 1227 | { 1228 | RTMP_Log(RTMP_LOGERROR, "Couldn't verify the client digest"); /* continuing anyway will probably fail */ 1229 | return FALSE; 1230 | } 1231 | } 1232 | 1233 | /* generate SWFVerification token (SHA256 HMAC hash of decompressed SWF, key are the last 32 bytes of the server handshake) */ 1234 | if (r->Link.SWFSize) 1235 | { 1236 | const char swfVerify[] = { 0x01, 0x01 }; 1237 | char *vend = r->Link.SWFVerificationResponse+sizeof(r->Link.SWFVerificationResponse); 1238 | 1239 | memcpy(r->Link.SWFVerificationResponse, swfVerify, 2); 1240 | AMF_EncodeInt32(&r->Link.SWFVerificationResponse[2], vend, r->Link.SWFSize); 1241 | AMF_EncodeInt32(&r->Link.SWFVerificationResponse[6], vend, r->Link.SWFSize); 1242 | HMACsha256(r->Link.SWFHash, SHA256_DIGEST_LENGTH, 1243 | &serversig[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH], 1244 | SHA256_DIGEST_LENGTH, 1245 | (uint8_t *)&r->Link.SWFVerificationResponse[10]); 1246 | } 1247 | 1248 | /* do Diffie-Hellmann Key exchange for encrypted RTMP */ 1249 | if (encrypted) 1250 | { 1251 | int dhposClient, len; 1252 | /* compute secret key */ 1253 | uint8_t secretKey[128] = { 0 }; 1254 | 1255 | dhposClient = getdh(clientsig, RTMP_SIG_SIZE); 1256 | RTMP_Log(RTMP_LOGDEBUG, "%s: Client DH public key offset: %d", __FUNCTION__, 1257 | dhposClient); 1258 | len = 1259 | DHComputeSharedSecretKey(r->Link.dh, 1260 | (uint8_t *) &clientsig[dhposClient], 128, 1261 | secretKey); 1262 | if (len < 0) 1263 | { 1264 | RTMP_Log(RTMP_LOGDEBUG, "%s: Wrong secret key position!", __FUNCTION__); 1265 | return FALSE; 1266 | } 1267 | 1268 | RTMP_Log(RTMP_LOGDEBUG, "%s: Secret key: ", __FUNCTION__); 1269 | RTMP_LogHex(RTMP_LOGDEBUG, secretKey, 128); 1270 | 1271 | InitRC4Encryption(secretKey, 1272 | (uint8_t *) &clientsig[dhposClient], 1273 | (uint8_t *) &serversig[dhposServer], 1274 | &keyIn, &keyOut); 1275 | } 1276 | 1277 | 1278 | /* calculate response now */ 1279 | signatureResp = clientsig+RTMP_SIG_SIZE-SHA256_DIGEST_LENGTH; 1280 | 1281 | HMACsha256(&clientsig[digestPosClient], SHA256_DIGEST_LENGTH, 1282 | GenuineFMSKey, sizeof(GenuineFMSKey), digestResp); 1283 | HMACsha256(clientsig, RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH, digestResp, 1284 | SHA256_DIGEST_LENGTH, signatureResp); 1285 | #ifdef FP10 1286 | if (type == 8 ) 1287 | { 1288 | uint8_t *dptr = digestResp; 1289 | uint8_t *sig = signatureResp; 1290 | /* encrypt signatureResp */ 1291 | for (i=0; iLink.rc4keyIn = keyIn; 1391 | r->Link.rc4keyOut = keyOut; 1392 | 1393 | /* update the keystreams */ 1394 | if (r->Link.rc4keyIn) 1395 | { 1396 | RC4_encrypt(r->Link.rc4keyIn, RTMP_SIG_SIZE, (uint8_t *) buff); 1397 | } 1398 | 1399 | if (r->Link.rc4keyOut) 1400 | { 1401 | RC4_encrypt(r->Link.rc4keyOut, RTMP_SIG_SIZE, (uint8_t *) buff); 1402 | } 1403 | } 1404 | } 1405 | else 1406 | { 1407 | if (memcmp(serversig, clientsig, RTMP_SIG_SIZE) != 0) 1408 | { 1409 | RTMP_Log(RTMP_LOGWARNING, "%s: client signature does not match!", 1410 | __FUNCTION__); 1411 | } 1412 | } 1413 | 1414 | RTMP_Log(RTMP_LOGDEBUG, "%s: Handshaking finished....", __FUNCTION__); 1415 | return TRUE; 1416 | } 1417 | -------------------------------------------------------------------------------- /librtmp/hashswf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2010 Howard Chu 3 | * 4 | * This file is part of librtmp. 5 | * 6 | * librtmp is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as 8 | * published by the Free Software Foundation; either version 2.1, 9 | * or (at your option) any later version. 10 | * 11 | * librtmp is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with librtmp see the file COPYING. If not, write to 18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 | * Boston, MA 02110-1301, USA. 20 | * http://www.gnu.org/copyleft/lgpl.html 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "rtmp_sys.h" 30 | #include "log.h" 31 | #include "http.h" 32 | 33 | #ifdef CRYPTO 34 | #ifdef USE_POLARSSL 35 | #include 36 | #ifndef SHA256_DIGEST_LENGTH 37 | #define SHA256_DIGEST_LENGTH 32 38 | #endif 39 | #define HMAC_CTX sha2_context 40 | #define HMAC_setup(ctx, key, len) sha2_hmac_starts(&ctx, (unsigned char *)key, len, 0) 41 | #define HMAC_crunch(ctx, buf, len) sha2_hmac_update(&ctx, buf, len) 42 | #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; sha2_hmac_finish(&ctx, dig) 43 | #define HMAC_close(ctx) 44 | #elif defined(USE_GNUTLS) 45 | #include 46 | #include 47 | #ifndef SHA256_DIGEST_LENGTH 48 | #define SHA256_DIGEST_LENGTH 32 49 | #endif 50 | #define HMAC_CTX gcry_md_hd_t 51 | #define HMAC_setup(ctx, key, len) gcry_md_open(&ctx, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); gcry_md_setkey(ctx, key, len) 52 | #define HMAC_crunch(ctx, buf, len) gcry_md_write(ctx, buf, len) 53 | #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; memcpy(dig, gcry_md_read(ctx, 0), dlen) 54 | #define HMAC_close(ctx) gcry_md_close(ctx) 55 | #else /* USE_OPENSSL */ 56 | #include 57 | #include 58 | #include 59 | #include 60 | #define HMAC_setup(ctx, key, len) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, (unsigned char *)key, len, EVP_sha256(), 0) 61 | #define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, (unsigned char *)buf, len) 62 | #define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, (unsigned char *)dig, &dlen); 63 | #define HMAC_close(ctx) HMAC_CTX_cleanup(&ctx) 64 | #endif 65 | 66 | extern void RTMP_TLS_Init(); 67 | extern TLS_CTX RTMP_TLS_ctx; 68 | 69 | #include 70 | 71 | #endif /* CRYPTO */ 72 | 73 | #define AGENT "Mozilla/5.0" 74 | 75 | HTTPResult 76 | HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb) 77 | { 78 | char *host, *path; 79 | char *p1, *p2; 80 | char hbuf[256]; 81 | int port = 80; 82 | #ifdef CRYPTO 83 | int ssl = 0; 84 | #endif 85 | int hlen, flen = 0; 86 | int rc, i; 87 | int len_known; 88 | HTTPResult ret = HTTPRES_OK; 89 | struct sockaddr_in sa; 90 | RTMPSockBuf sb = {0}; 91 | 92 | http->status = -1; 93 | 94 | memset(&sa, 0, sizeof(struct sockaddr_in)); 95 | sa.sin_family = AF_INET; 96 | 97 | /* we only handle http here */ 98 | if (strncasecmp(url, "http", 4)) 99 | return HTTPRES_BAD_REQUEST; 100 | 101 | if (url[4] == 's') 102 | { 103 | #ifdef CRYPTO 104 | ssl = 1; 105 | port = 443; 106 | if (!RTMP_TLS_ctx) 107 | RTMP_TLS_Init(); 108 | #else 109 | return HTTPRES_BAD_REQUEST; 110 | #endif 111 | } 112 | 113 | p1 = strchr(url + 4, ':'); 114 | if (!p1 || strncmp(p1, "://", 3)) 115 | return HTTPRES_BAD_REQUEST; 116 | 117 | host = p1 + 3; 118 | path = strchr(host, '/'); 119 | hlen = path - host; 120 | strncpy(hbuf, host, hlen); 121 | hbuf[hlen] = '\0'; 122 | host = hbuf; 123 | p1 = strrchr(host, ':'); 124 | if (p1) 125 | { 126 | *p1++ = '\0'; 127 | port = atoi(p1); 128 | } 129 | 130 | sa.sin_addr.s_addr = inet_addr(host); 131 | if (sa.sin_addr.s_addr == INADDR_NONE) 132 | { 133 | struct hostent *hp = gethostbyname(host); 134 | if (!hp || !hp->h_addr) 135 | return HTTPRES_LOST_CONNECTION; 136 | sa.sin_addr = *(struct in_addr *)hp->h_addr; 137 | } 138 | sa.sin_port = htons(port); 139 | sb.sb_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 140 | if (sb.sb_socket == -1) 141 | return HTTPRES_LOST_CONNECTION; 142 | i = 143 | sprintf(sb.sb_buf, 144 | "GET %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\nReferrer: %.*s\r\n", 145 | path, AGENT, host, (int)(path - url + 1), url); 146 | if (http->date[0]) 147 | i += sprintf(sb.sb_buf + i, "If-Modified-Since: %s\r\n", http->date); 148 | i += sprintf(sb.sb_buf + i, "\r\n"); 149 | 150 | if (connect 151 | (sb.sb_socket, (struct sockaddr *)&sa, sizeof(struct sockaddr)) < 0) 152 | { 153 | ret = HTTPRES_LOST_CONNECTION; 154 | goto leave; 155 | } 156 | #ifdef CRYPTO 157 | if (ssl) 158 | { 159 | #ifdef NO_SSL 160 | RTMP_Log(RTMP_LOGERROR, "%s, No SSL/TLS support", __FUNCTION__); 161 | ret = HTTPRES_BAD_REQUEST; 162 | goto leave; 163 | #else 164 | TLS_client(RTMP_TLS_ctx, sb.sb_ssl); 165 | TLS_setfd(sb.sb_ssl, sb.sb_socket); 166 | if ((i = TLS_connect(sb.sb_ssl)) < 0) 167 | { 168 | RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__); 169 | ret = HTTPRES_LOST_CONNECTION; 170 | goto leave; 171 | } 172 | #endif 173 | } 174 | #endif 175 | RTMPSockBuf_Send(&sb, sb.sb_buf, i); 176 | 177 | /* set timeout */ 178 | #define HTTP_TIMEOUT 5 179 | { 180 | SET_RCVTIMEO(tv, HTTP_TIMEOUT); 181 | if (setsockopt 182 | (sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv))) 183 | { 184 | RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %ds failed!", 185 | __FUNCTION__, HTTP_TIMEOUT); 186 | } 187 | } 188 | 189 | sb.sb_size = 0; 190 | sb.sb_timedout = FALSE; 191 | if (RTMPSockBuf_Fill(&sb) < 1) 192 | { 193 | ret = HTTPRES_LOST_CONNECTION; 194 | goto leave; 195 | } 196 | if (strncmp(sb.sb_buf, "HTTP/1", 6)) 197 | { 198 | ret = HTTPRES_BAD_REQUEST; 199 | goto leave; 200 | } 201 | 202 | p1 = strchr(sb.sb_buf, ' '); 203 | rc = atoi(p1 + 1); 204 | http->status = rc; 205 | 206 | if (rc >= 300) 207 | { 208 | if (rc == 304) 209 | { 210 | ret = HTTPRES_OK_NOT_MODIFIED; 211 | goto leave; 212 | } 213 | else if (rc == 404) 214 | ret = HTTPRES_NOT_FOUND; 215 | else if (rc >= 500) 216 | ret = HTTPRES_SERVER_ERROR; 217 | else if (rc >= 400) 218 | ret = HTTPRES_BAD_REQUEST; 219 | else 220 | ret = HTTPRES_REDIRECTED; 221 | } 222 | 223 | p1 = memchr(sb.sb_buf, '\n', sb.sb_size); 224 | if (!p1) 225 | { 226 | ret = HTTPRES_BAD_REQUEST; 227 | goto leave; 228 | } 229 | sb.sb_start = p1 + 1; 230 | sb.sb_size -= sb.sb_start - sb.sb_buf; 231 | 232 | while ((p2 = memchr(sb.sb_start, '\r', sb.sb_size))) 233 | { 234 | if (*sb.sb_start == '\r') 235 | { 236 | sb.sb_start += 2; 237 | sb.sb_size -= 2; 238 | break; 239 | } 240 | else 241 | if (!strncasecmp 242 | (sb.sb_start, "Content-Length: ", sizeof("Content-Length: ") - 1)) 243 | { 244 | flen = atoi(sb.sb_start + sizeof("Content-Length: ") - 1); 245 | } 246 | else 247 | if (!strncasecmp 248 | (sb.sb_start, "Last-Modified: ", sizeof("Last-Modified: ") - 1)) 249 | { 250 | *p2 = '\0'; 251 | strcpy(http->date, sb.sb_start + sizeof("Last-Modified: ") - 1); 252 | } 253 | p2 += 2; 254 | sb.sb_size -= p2 - sb.sb_start; 255 | sb.sb_start = p2; 256 | if (sb.sb_size < 1) 257 | { 258 | if (RTMPSockBuf_Fill(&sb) < 1) 259 | { 260 | ret = HTTPRES_LOST_CONNECTION; 261 | goto leave; 262 | } 263 | } 264 | } 265 | 266 | len_known = flen > 0; 267 | while ((!len_known || flen > 0) && 268 | (sb.sb_size > 0 || RTMPSockBuf_Fill(&sb) > 0)) 269 | { 270 | cb(sb.sb_start, 1, sb.sb_size, http->data); 271 | if (len_known) 272 | flen -= sb.sb_size; 273 | http->size += sb.sb_size; 274 | sb.sb_size = 0; 275 | } 276 | 277 | if (flen > 0) 278 | ret = HTTPRES_LOST_CONNECTION; 279 | 280 | leave: 281 | RTMPSockBuf_Close(&sb); 282 | return ret; 283 | } 284 | 285 | #ifdef CRYPTO 286 | 287 | #define CHUNK 16384 288 | 289 | struct info 290 | { 291 | z_stream *zs; 292 | HMAC_CTX ctx; 293 | int first; 294 | int zlib; 295 | int size; 296 | }; 297 | 298 | static size_t 299 | swfcrunch(void *ptr, size_t size, size_t nmemb, void *stream) 300 | { 301 | struct info *i = stream; 302 | char *p = ptr; 303 | size_t len = size * nmemb; 304 | 305 | if (i->first) 306 | { 307 | i->first = 0; 308 | /* compressed? */ 309 | if (!strncmp(p, "CWS", 3)) 310 | { 311 | *p = 'F'; 312 | i->zlib = 1; 313 | } 314 | HMAC_crunch(i->ctx, (unsigned char *)p, 8); 315 | p += 8; 316 | len -= 8; 317 | i->size = 8; 318 | } 319 | 320 | if (i->zlib) 321 | { 322 | unsigned char out[CHUNK]; 323 | i->zs->next_in = (unsigned char *)p; 324 | i->zs->avail_in = len; 325 | do 326 | { 327 | i->zs->avail_out = CHUNK; 328 | i->zs->next_out = out; 329 | inflate(i->zs, Z_NO_FLUSH); 330 | len = CHUNK - i->zs->avail_out; 331 | i->size += len; 332 | HMAC_crunch(i->ctx, out, len); 333 | } 334 | while (i->zs->avail_out == 0); 335 | } 336 | else 337 | { 338 | i->size += len; 339 | HMAC_crunch(i->ctx, (unsigned char *)p, len); 340 | } 341 | return size * nmemb; 342 | } 343 | 344 | static int tzoff; 345 | static int tzchecked; 346 | 347 | #define JAN02_1980 318340800 348 | 349 | static const char *monthtab[12] = { "Jan", "Feb", "Mar", 350 | "Apr", "May", "Jun", 351 | "Jul", "Aug", "Sep", 352 | "Oct", "Nov", "Dec" 353 | }; 354 | static const char *days[] = 355 | { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; 356 | 357 | /* Parse an HTTP datestamp into Unix time */ 358 | static time_t 359 | make_unix_time(char *s) 360 | { 361 | struct tm time; 362 | int i, ysub = 1900, fmt = 0; 363 | char *month; 364 | char *n; 365 | time_t res; 366 | 367 | if (s[3] != ' ') 368 | { 369 | fmt = 1; 370 | if (s[3] != ',') 371 | ysub = 0; 372 | } 373 | for (n = s; *n; ++n) 374 | if (*n == '-' || *n == ':') 375 | *n = ' '; 376 | 377 | time.tm_mon = 0; 378 | n = strchr(s, ' '); 379 | if (fmt) 380 | { 381 | /* Day, DD-MMM-YYYY HH:MM:SS GMT */ 382 | time.tm_mday = strtol(n + 1, &n, 0); 383 | month = n + 1; 384 | n = strchr(month, ' '); 385 | time.tm_year = strtol(n + 1, &n, 0); 386 | time.tm_hour = strtol(n + 1, &n, 0); 387 | time.tm_min = strtol(n + 1, &n, 0); 388 | time.tm_sec = strtol(n + 1, NULL, 0); 389 | } 390 | else 391 | { 392 | /* Unix ctime() format. Does not conform to HTTP spec. */ 393 | /* Day MMM DD HH:MM:SS YYYY */ 394 | month = n + 1; 395 | n = strchr(month, ' '); 396 | while (isspace(*n)) 397 | n++; 398 | time.tm_mday = strtol(n, &n, 0); 399 | time.tm_hour = strtol(n + 1, &n, 0); 400 | time.tm_min = strtol(n + 1, &n, 0); 401 | time.tm_sec = strtol(n + 1, &n, 0); 402 | time.tm_year = strtol(n + 1, NULL, 0); 403 | } 404 | if (time.tm_year > 100) 405 | time.tm_year -= ysub; 406 | 407 | for (i = 0; i < 12; i++) 408 | if (!strncasecmp(month, monthtab[i], 3)) 409 | { 410 | time.tm_mon = i; 411 | break; 412 | } 413 | time.tm_isdst = 0; /* daylight saving is never in effect in GMT */ 414 | 415 | /* this is normally the value of extern int timezone, but some 416 | * braindead C libraries don't provide it. 417 | */ 418 | if (!tzchecked) 419 | { 420 | struct tm *tc; 421 | time_t then = JAN02_1980; 422 | tc = localtime(&then); 423 | tzoff = (12 - tc->tm_hour) * 3600 + tc->tm_min * 60 + tc->tm_sec; 424 | tzchecked = 1; 425 | } 426 | res = mktime(&time); 427 | /* Unfortunately, mktime() assumes the input is in local time, 428 | * not GMT, so we have to correct it here. 429 | */ 430 | if (res != -1) 431 | res += tzoff; 432 | return res; 433 | } 434 | 435 | /* Convert a Unix time to a network time string 436 | * Weekday, DD-MMM-YYYY HH:MM:SS GMT 437 | */ 438 | static void 439 | strtime(time_t * t, char *s) 440 | { 441 | struct tm *tm; 442 | 443 | tm = gmtime((time_t *) t); 444 | sprintf(s, "%s, %02d %s %d %02d:%02d:%02d GMT", 445 | days[tm->tm_wday], tm->tm_mday, monthtab[tm->tm_mon], 446 | tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec); 447 | } 448 | 449 | #define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf)) 450 | 451 | int 452 | RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash, 453 | int age) 454 | { 455 | FILE *f = NULL; 456 | char *path, date[64], cctim[64]; 457 | long pos = 0; 458 | time_t ctim = -1, cnow; 459 | int i, got = 0, ret = 0; 460 | unsigned int hlen; 461 | struct info in = { 0 }; 462 | struct HTTP_ctx http = { 0 }; 463 | HTTPResult httpres; 464 | z_stream zs = { 0 }; 465 | AVal home, hpre; 466 | 467 | date[0] = '\0'; 468 | #ifdef _WIN32 469 | #ifdef XBMC4XBOX 470 | hpre.av_val = "Q:"; 471 | hpre.av_len = 2; 472 | home.av_val = "\\UserData"; 473 | #else 474 | hpre.av_val = getenv("HOMEDRIVE"); 475 | hpre.av_len = strlen(hpre.av_val); 476 | home.av_val = getenv("HOMEPATH"); 477 | #endif 478 | #define DIRSEP "\\" 479 | 480 | #else /* !_WIN32 */ 481 | hpre.av_val = ""; 482 | hpre.av_len = 0; 483 | home.av_val = getenv("HOME"); 484 | #define DIRSEP "/" 485 | #endif 486 | if (!home.av_val) 487 | home.av_val = "."; 488 | home.av_len = strlen(home.av_val); 489 | 490 | /* SWF hash info is cached in a fixed-format file. 491 | * url: 492 | * ctim: HTTP datestamp of when we last checked it. 493 | * date: HTTP datestamp of the SWF's last modification. 494 | * size: SWF size in hex 495 | * hash: SWF hash in hex 496 | * 497 | * These fields must be present in this order. All fields 498 | * besides URL are fixed size. 499 | */ 500 | path = malloc(hpre.av_len + home.av_len + sizeof(DIRSEP ".swfinfo")); 501 | sprintf(path, "%s%s" DIRSEP ".swfinfo", hpre.av_val, home.av_val); 502 | 503 | f = fopen(path, "r+"); 504 | while (f) 505 | { 506 | char buf[4096], *file, *p; 507 | 508 | file = strchr(url, '/'); 509 | if (!file) 510 | break; 511 | file += 2; 512 | file = strchr(file, '/'); 513 | if (!file) 514 | break; 515 | file++; 516 | hlen = file - url; 517 | p = strrchr(file, '/'); 518 | if (p) 519 | file = p; 520 | else 521 | file--; 522 | 523 | while (fgets(buf, sizeof(buf), f)) 524 | { 525 | char *r1; 526 | 527 | got = 0; 528 | 529 | if (strncmp(buf, "url: ", 5)) 530 | continue; 531 | if (strncmp(buf + 5, url, hlen)) 532 | continue; 533 | r1 = strrchr(buf, '/'); 534 | i = strlen(r1); 535 | r1[--i] = '\0'; 536 | if (strncmp(r1, file, i)) 537 | continue; 538 | pos = ftell(f); 539 | while (got < 4 && fgets(buf, sizeof(buf), f)) 540 | { 541 | if (!strncmp(buf, "size: ", 6)) 542 | { 543 | *size = strtol(buf + 6, NULL, 16); 544 | got++; 545 | } 546 | else if (!strncmp(buf, "hash: ", 6)) 547 | { 548 | unsigned char *ptr = hash, *in = (unsigned char *)buf + 6; 549 | int l = strlen((char *)in) - 1; 550 | for (i = 0; i < l; i += 2) 551 | *ptr++ = (HEX2BIN(in[i]) << 4) | HEX2BIN(in[i + 1]); 552 | got++; 553 | } 554 | else if (!strncmp(buf, "date: ", 6)) 555 | { 556 | buf[strlen(buf) - 1] = '\0'; 557 | strncpy(date, buf + 6, sizeof(date)); 558 | got++; 559 | } 560 | else if (!strncmp(buf, "ctim: ", 6)) 561 | { 562 | buf[strlen(buf) - 1] = '\0'; 563 | ctim = make_unix_time(buf + 6); 564 | got++; 565 | } 566 | else if (!strncmp(buf, "url: ", 5)) 567 | break; 568 | } 569 | break; 570 | } 571 | break; 572 | } 573 | 574 | cnow = time(NULL); 575 | /* If we got a cache time, see if it's young enough to use directly */ 576 | if (age && ctim > 0) 577 | { 578 | ctim = cnow - ctim; 579 | ctim /= 3600 * 24; /* seconds to days */ 580 | if (ctim < age) /* ok, it's new enough */ 581 | goto out; 582 | } 583 | 584 | in.first = 1; 585 | HMAC_setup(in.ctx, "Genuine Adobe Flash Player 001", 30); 586 | inflateInit(&zs); 587 | in.zs = &zs; 588 | 589 | http.date = date; 590 | http.data = ∈ 591 | 592 | httpres = HTTP_get(&http, url, swfcrunch); 593 | 594 | inflateEnd(&zs); 595 | 596 | if (httpres != HTTPRES_OK && httpres != HTTPRES_OK_NOT_MODIFIED) 597 | { 598 | ret = -1; 599 | if (httpres == HTTPRES_LOST_CONNECTION) 600 | RTMP_Log(RTMP_LOGERROR, "%s: connection lost while downloading swfurl %s", 601 | __FUNCTION__, url); 602 | else if (httpres == HTTPRES_NOT_FOUND) 603 | RTMP_Log(RTMP_LOGERROR, "%s: swfurl %s not found", __FUNCTION__, url); 604 | else 605 | RTMP_Log(RTMP_LOGERROR, "%s: couldn't contact swfurl %s (HTTP error %d)", 606 | __FUNCTION__, url, http.status); 607 | } 608 | else 609 | { 610 | if (got && pos) 611 | fseek(f, pos, SEEK_SET); 612 | else 613 | { 614 | char *q; 615 | if (!f) 616 | f = fopen(path, "w"); 617 | if (!f) 618 | { 619 | int err = errno; 620 | RTMP_Log(RTMP_LOGERROR, 621 | "%s: couldn't open %s for writing, errno %d (%s)", 622 | __FUNCTION__, path, err, strerror(err)); 623 | ret = -1; 624 | goto out; 625 | } 626 | fseek(f, 0, SEEK_END); 627 | q = strchr(url, '?'); 628 | if (q) 629 | i = q - url; 630 | else 631 | i = strlen(url); 632 | 633 | fprintf(f, "url: %.*s\n", i, url); 634 | } 635 | strtime(&cnow, cctim); 636 | fprintf(f, "ctim: %s\n", cctim); 637 | 638 | if (!in.first) 639 | { 640 | HMAC_finish(in.ctx, hash, hlen); 641 | *size = in.size; 642 | 643 | fprintf(f, "date: %s\n", date); 644 | fprintf(f, "size: %08x\n", in.size); 645 | fprintf(f, "hash: "); 646 | for (i = 0; i < SHA256_DIGEST_LENGTH; i++) 647 | fprintf(f, "%02x", hash[i]); 648 | fprintf(f, "\n"); 649 | } 650 | } 651 | HMAC_close(in.ctx); 652 | out: 653 | free(path); 654 | if (f) 655 | fclose(f); 656 | return ret; 657 | } 658 | #else 659 | int 660 | RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash, 661 | int age) 662 | { 663 | return -1; 664 | } 665 | #endif 666 | -------------------------------------------------------------------------------- /librtmp/http.h: -------------------------------------------------------------------------------- 1 | #ifndef __RTMP_HTTP_H__ 2 | #define __RTMP_HTTP_H__ 3 | /* 4 | * Copyright (C) 2010 Howard Chu 5 | * Copyright (C) 2010 Antti Ajanki 6 | * 7 | * This file is part of librtmp. 8 | * 9 | * librtmp is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation; either version 2.1, 12 | * or (at your option) any later version. 13 | * 14 | * librtmp is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public License 20 | * along with librtmp see the file COPYING. If not, write to 21 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | * Boston, MA 02110-1301, USA. 23 | * http://www.gnu.org/copyleft/lgpl.html 24 | */ 25 | 26 | typedef enum { 27 | HTTPRES_OK, /* result OK */ 28 | HTTPRES_OK_NOT_MODIFIED, /* not modified since last request */ 29 | HTTPRES_NOT_FOUND, /* not found */ 30 | HTTPRES_BAD_REQUEST, /* client error */ 31 | HTTPRES_SERVER_ERROR, /* server reported an error */ 32 | HTTPRES_REDIRECTED, /* resource has been moved */ 33 | HTTPRES_LOST_CONNECTION /* connection lost while waiting for data */ 34 | } HTTPResult; 35 | 36 | struct HTTP_ctx { 37 | char *date; 38 | int size; 39 | int status; 40 | void *data; 41 | }; 42 | 43 | typedef size_t (HTTP_read_callback)(void *ptr, size_t size, size_t nmemb, void *stream); 44 | 45 | HTTPResult HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /librtmp/librtmp-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'librtmp' target in the 'librtmp' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /librtmp/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008-2009 Andrej Stepanchuk 3 | * Copyright (C) 2009-2010 Howard Chu 4 | * 5 | * This file is part of librtmp. 6 | * 7 | * librtmp is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU Lesser General Public License as 9 | * published by the Free Software Foundation; either version 2.1, 10 | * or (at your option) any later version. 11 | * 12 | * librtmp is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with librtmp see the file COPYING. If not, write to 19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 | * Boston, MA 02110-1301, USA. 21 | * http://www.gnu.org/copyleft/lgpl.html 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "rtmp_sys.h" 31 | #include "log.h" 32 | 33 | #define MAX_PRINT_LEN 2048 34 | 35 | RTMP_LogLevel RTMP_debuglevel = RTMP_LOGERROR; 36 | 37 | static int neednl; 38 | 39 | static FILE *fmsg; 40 | 41 | static RTMP_LogCallback rtmp_log_default, *cb = rtmp_log_default; 42 | 43 | static const char *levels[] = { 44 | "CRIT", "ERROR", "WARNING", "INFO", 45 | "DEBUG", "DEBUG2" 46 | }; 47 | 48 | static void rtmp_log_default(int level, const char *format, va_list vl) 49 | { 50 | char str[MAX_PRINT_LEN]=""; 51 | 52 | vsnprintf(str, MAX_PRINT_LEN-1, format, vl); 53 | 54 | /* Filter out 'no-name' */ 55 | if ( RTMP_debuglevel RTMP_debuglevel ) 108 | return; 109 | 110 | ptr = line; 111 | 112 | for(i=0; i> 4)]; 114 | *ptr++ = hexdig[0x0f & data[i]]; 115 | if ((i & 0x0f) == 0x0f) { 116 | *ptr = '\0'; 117 | ptr = line; 118 | RTMP_Log(level, "%s", line); 119 | } else { 120 | *ptr++ = ' '; 121 | } 122 | } 123 | if (i & 0x0f) { 124 | *ptr = '\0'; 125 | RTMP_Log(level, "%s", line); 126 | } 127 | } 128 | 129 | void RTMP_LogHexString(int level, const uint8_t *data, unsigned long len) 130 | { 131 | #define BP_OFFSET 9 132 | #define BP_GRAPH 60 133 | #define BP_LEN 80 134 | char line[BP_LEN]; 135 | unsigned long i; 136 | 137 | if ( !data || level > RTMP_debuglevel ) 138 | return; 139 | 140 | /* in case len is zero */ 141 | line[0] = '\0'; 142 | 143 | for ( i = 0 ; i < len ; i++ ) { 144 | int n = i % 16; 145 | unsigned off; 146 | 147 | if( !n ) { 148 | if( i ) RTMP_Log( level, "%s", line ); 149 | memset( line, ' ', sizeof(line)-2 ); 150 | line[sizeof(line)-2] = '\0'; 151 | 152 | off = i % 0x0ffffU; 153 | 154 | line[2] = hexdig[0x0f & (off >> 12)]; 155 | line[3] = hexdig[0x0f & (off >> 8)]; 156 | line[4] = hexdig[0x0f & (off >> 4)]; 157 | line[5] = hexdig[0x0f & off]; 158 | line[6] = ':'; 159 | } 160 | 161 | off = BP_OFFSET + n*3 + ((n >= 8)?1:0); 162 | line[off] = hexdig[0x0f & ( data[i] >> 4 )]; 163 | line[off+1] = hexdig[0x0f & data[i]]; 164 | 165 | off = BP_GRAPH + n + ((n >= 8)?1:0); 166 | 167 | if ( isprint( data[i] )) { 168 | line[BP_GRAPH + n] = data[i]; 169 | } else { 170 | line[BP_GRAPH + n] = '.'; 171 | } 172 | } 173 | 174 | RTMP_Log( level, "%s", line ); 175 | } 176 | 177 | /* These should only be used by apps, never by the library itself */ 178 | void RTMP_LogPrintf(const char *format, ...) 179 | { 180 | char str[MAX_PRINT_LEN]=""; 181 | int len; 182 | va_list args; 183 | va_start(args, format); 184 | len = vsnprintf(str, MAX_PRINT_LEN-1, format, args); 185 | va_end(args); 186 | 187 | if ( RTMP_debuglevel==RTMP_LOGCRIT ) 188 | return; 189 | 190 | if ( !fmsg ) fmsg = stderr; 191 | 192 | if (neednl) { 193 | putc('\n', fmsg); 194 | neednl = 0; 195 | } 196 | 197 | if (len > MAX_PRINT_LEN-1) 198 | len = MAX_PRINT_LEN-1; 199 | fprintf(fmsg, "%s", str); 200 | if (str[len-1] == '\n') 201 | fflush(fmsg); 202 | } 203 | 204 | void RTMP_LogStatus(const char *format, ...) 205 | { 206 | char str[MAX_PRINT_LEN]=""; 207 | va_list args; 208 | va_start(args, format); 209 | vsnprintf(str, MAX_PRINT_LEN-1, format, args); 210 | va_end(args); 211 | 212 | if ( RTMP_debuglevel==RTMP_LOGCRIT ) 213 | return; 214 | 215 | if ( !fmsg ) fmsg = stderr; 216 | 217 | fprintf(fmsg, "%s", str); 218 | fflush(fmsg); 219 | neednl = 1; 220 | } 221 | -------------------------------------------------------------------------------- /librtmp/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008-2009 Andrej Stepanchuk 3 | * Copyright (C) 2009-2010 Howard Chu 4 | * 5 | * This file is part of librtmp. 6 | * 7 | * librtmp is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU Lesser General Public License as 9 | * published by the Free Software Foundation; either version 2.1, 10 | * or (at your option) any later version. 11 | * 12 | * librtmp is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with librtmp see the file COPYING. If not, write to 19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 | * Boston, MA 02110-1301, USA. 21 | * http://www.gnu.org/copyleft/lgpl.html 22 | */ 23 | 24 | #ifndef __RTMP_LOG_H__ 25 | #define __RTMP_LOG_H__ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | /* Enable this to get full debugging output */ 35 | /* #define _DEBUG */ 36 | 37 | #ifdef _DEBUG 38 | #undef NODEBUG 39 | #endif 40 | 41 | typedef enum 42 | { RTMP_LOGCRIT=0, RTMP_LOGERROR, RTMP_LOGWARNING, RTMP_LOGINFO, 43 | RTMP_LOGDEBUG, RTMP_LOGDEBUG2, RTMP_LOGALL 44 | } RTMP_LogLevel; 45 | 46 | extern RTMP_LogLevel RTMP_debuglevel; 47 | 48 | typedef void (RTMP_LogCallback)(int level, const char *fmt, va_list); 49 | void RTMP_LogSetCallback(RTMP_LogCallback *cb); 50 | void RTMP_LogSetOutput(FILE *file); 51 | void RTMP_LogPrintf(const char *format, ...); 52 | void RTMP_LogStatus(const char *format, ...); 53 | void RTMP_Log(int level, const char *format, ...); 54 | void RTMP_LogHex(int level, const uint8_t *data, unsigned long len); 55 | void RTMP_LogHexString(int level, const uint8_t *data, unsigned long len); 56 | void RTMP_LogSetLevel(RTMP_LogLevel lvl); 57 | RTMP_LogLevel RTMP_LogGetLevel(void); 58 | 59 | #ifdef __cplusplus 60 | } 61 | #endif 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /librtmp/parseurl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 Andrej Stepanchuk 3 | * Copyright (C) 2009-2010 Howard Chu 4 | * 5 | * This file is part of librtmp. 6 | * 7 | * librtmp is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU Lesser General Public License as 9 | * published by the Free Software Foundation; either version 2.1, 10 | * or (at your option) any later version. 11 | * 12 | * librtmp is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with librtmp see the file COPYING. If not, write to 19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 | * Boston, MA 02110-1301, USA. 21 | * http://www.gnu.org/copyleft/lgpl.html 22 | */ 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | #include "rtmp_sys.h" 31 | #include "log.h" 32 | 33 | int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port, 34 | AVal *playpath, AVal *app) 35 | { 36 | char *p, *end, *col, *ques, *slash; 37 | 38 | RTMP_Log(RTMP_LOGDEBUG, "Parsing..."); 39 | 40 | *protocol = RTMP_PROTOCOL_RTMP; 41 | *port = 0; 42 | playpath->av_len = 0; 43 | playpath->av_val = NULL; 44 | app->av_len = 0; 45 | app->av_val = NULL; 46 | 47 | /* Old School Parsing */ 48 | 49 | /* look for usual :// pattern */ 50 | p = strstr(url, "://"); 51 | if(!p) { 52 | RTMP_Log(RTMP_LOGERROR, "RTMP URL: No :// in url!"); 53 | return FALSE; 54 | } 55 | { 56 | int len = (int)(p-url); 57 | 58 | if(len == 4 && strncasecmp(url, "rtmp", 4)==0) 59 | *protocol = RTMP_PROTOCOL_RTMP; 60 | else if(len == 5 && strncasecmp(url, "rtmpt", 5)==0) 61 | *protocol = RTMP_PROTOCOL_RTMPT; 62 | else if(len == 5 && strncasecmp(url, "rtmps", 5)==0) 63 | *protocol = RTMP_PROTOCOL_RTMPS; 64 | else if(len == 5 && strncasecmp(url, "rtmpe", 5)==0) 65 | *protocol = RTMP_PROTOCOL_RTMPE; 66 | else if(len == 5 && strncasecmp(url, "rtmfp", 5)==0) 67 | *protocol = RTMP_PROTOCOL_RTMFP; 68 | else if(len == 6 && strncasecmp(url, "rtmpte", 6)==0) 69 | *protocol = RTMP_PROTOCOL_RTMPTE; 70 | else if(len == 6 && strncasecmp(url, "rtmpts", 6)==0) 71 | *protocol = RTMP_PROTOCOL_RTMPTS; 72 | else { 73 | RTMP_Log(RTMP_LOGWARNING, "Unknown protocol!\n"); 74 | goto parsehost; 75 | } 76 | } 77 | 78 | RTMP_Log(RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol); 79 | 80 | parsehost: 81 | /* let's get the hostname */ 82 | p+=3; 83 | 84 | /* check for sudden death */ 85 | if(*p==0) { 86 | RTMP_Log(RTMP_LOGWARNING, "No hostname in URL!"); 87 | return FALSE; 88 | } 89 | 90 | end = p + strlen(p); 91 | col = strchr(p, ':'); 92 | ques = strchr(p, '?'); 93 | slash = strchr(p, '/'); 94 | 95 | { 96 | int hostlen; 97 | if(slash) 98 | hostlen = slash - p; 99 | else 100 | hostlen = end - p; 101 | if(col && col -p < hostlen) 102 | hostlen = col - p; 103 | 104 | if(hostlen < 256) { 105 | host->av_val = p; 106 | host->av_len = hostlen; 107 | RTMP_Log(RTMP_LOGDEBUG, "Parsed host : %.*s", hostlen, host->av_val); 108 | } else { 109 | RTMP_Log(RTMP_LOGWARNING, "Hostname exceeds 255 characters!"); 110 | } 111 | 112 | p+=hostlen; 113 | } 114 | 115 | /* get the port number if available */ 116 | if(*p == ':') { 117 | unsigned int p2; 118 | p++; 119 | p2 = atoi(p); 120 | if(p2 > 65535) { 121 | RTMP_Log(RTMP_LOGWARNING, "Invalid port number!"); 122 | } else { 123 | *port = p2; 124 | } 125 | } 126 | 127 | if(!slash) { 128 | RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!"); 129 | return TRUE; 130 | } 131 | p = slash+1; 132 | 133 | { 134 | /* parse application 135 | * 136 | * rtmp://host[:port]/app[/appinstance][/...] 137 | * application = app[/appinstance] 138 | */ 139 | 140 | char *slash2, *slash3 = NULL; 141 | int applen, appnamelen; 142 | 143 | slash2 = strchr(p, '/'); 144 | if(slash2) 145 | slash3 = strchr(slash2+1, '/'); 146 | 147 | applen = end-p; /* ondemand, pass all parameters as app */ 148 | appnamelen = applen; /* ondemand length */ 149 | 150 | if(ques && strstr(p, "slist=")) { /* whatever it is, the '?' and slist= means we need to use everything as app and parse plapath from slist= */ 151 | appnamelen = ques-p; 152 | } 153 | else if(strncmp(p, "ondemand/", 9)==0) { 154 | /* app = ondemand/foobar, only pass app=ondemand */ 155 | applen = 8; 156 | appnamelen = 8; 157 | } 158 | else { /* app!=ondemand, so app is app[/appinstance] */ 159 | if(slash3) 160 | appnamelen = slash3-p; 161 | else if(slash2) 162 | appnamelen = slash2-p; 163 | 164 | applen = appnamelen; 165 | } 166 | 167 | app->av_val = p; 168 | app->av_len = applen; 169 | RTMP_Log(RTMP_LOGDEBUG, "Parsed app : %.*s", applen, p); 170 | 171 | p += appnamelen; 172 | } 173 | 174 | if (*p == '/') 175 | p++; 176 | 177 | if (end-p) { 178 | AVal av = {p, end-p}; 179 | RTMP_ParsePlaypath(&av, playpath); 180 | } 181 | 182 | return TRUE; 183 | } 184 | 185 | /* 186 | * Extracts playpath from RTMP URL. playpath is the file part of the 187 | * URL, i.e. the part that comes after rtmp://host:port/app/ 188 | * 189 | * Returns the stream name in a format understood by FMS. The name is 190 | * the playpath part of the URL with formatting depending on the stream 191 | * type: 192 | * 193 | * mp4 streams: prepend "mp4:", remove extension 194 | * mp3 streams: prepend "mp3:", remove extension 195 | * flv streams: remove extension 196 | */ 197 | void RTMP_ParsePlaypath(AVal *in, AVal *out) { 198 | int addMP4 = 0; 199 | int addMP3 = 0; 200 | int subExt = 0; 201 | const char *playpath = in->av_val; 202 | const char *temp, *q, *ext = NULL; 203 | const char *ppstart = playpath; 204 | char *streamname, *destptr, *p; 205 | 206 | int pplen = in->av_len; 207 | 208 | out->av_val = NULL; 209 | out->av_len = 0; 210 | 211 | if ((*ppstart == '?') && 212 | (temp=strstr(ppstart, "slist=")) != 0) { 213 | ppstart = temp+6; 214 | pplen = strlen(ppstart); 215 | 216 | temp = strchr(ppstart, '&'); 217 | if (temp) { 218 | pplen = temp-ppstart; 219 | } 220 | } 221 | 222 | q = strchr(ppstart, '?'); 223 | if (pplen >= 4) { 224 | if (q) 225 | ext = q-4; 226 | else 227 | ext = &ppstart[pplen-4]; 228 | if ((strncmp(ext, ".f4v", 4) == 0) || 229 | (strncmp(ext, ".mp4", 4) == 0)) { 230 | addMP4 = 1; 231 | subExt = 1; 232 | /* Only remove .flv from rtmp URL, not slist params */ 233 | } else if ((ppstart == playpath) && 234 | (strncmp(ext, ".flv", 4) == 0)) { 235 | subExt = 1; 236 | } else if (strncmp(ext, ".mp3", 4) == 0) { 237 | addMP3 = 1; 238 | subExt = 1; 239 | } 240 | } 241 | 242 | streamname = (char *)malloc((pplen+4+1)*sizeof(char)); 243 | if (!streamname) 244 | return; 245 | 246 | destptr = streamname; 247 | if (addMP4) { 248 | if (strncmp(ppstart, "mp4:", 4)) { 249 | strcpy(destptr, "mp4:"); 250 | destptr += 4; 251 | } else { 252 | subExt = 0; 253 | } 254 | } else if (addMP3) { 255 | if (strncmp(ppstart, "mp3:", 4)) { 256 | strcpy(destptr, "mp3:"); 257 | destptr += 4; 258 | } else { 259 | subExt = 0; 260 | } 261 | } 262 | 263 | for (p=(char *)ppstart; pplen >0;) { 264 | /* skip extension */ 265 | if (subExt && p == ext) { 266 | p += 4; 267 | pplen -= 4; 268 | continue; 269 | } 270 | if (*p == '%') { 271 | unsigned int c; 272 | sscanf(p+1, "%02x", &c); 273 | *destptr++ = c; 274 | pplen -= 3; 275 | p += 3; 276 | } else { 277 | *destptr++ = *p++; 278 | pplen--; 279 | } 280 | } 281 | *destptr = '\0'; 282 | 283 | out->av_val = streamname; 284 | out->av_len = destptr - streamname; 285 | } 286 | -------------------------------------------------------------------------------- /librtmp/rtmp.h: -------------------------------------------------------------------------------- 1 | #ifndef __RTMP_H__ 2 | #define __RTMP_H__ 3 | /* 4 | * Copyright (C) 2005-2008 Team XBMC 5 | * http://www.xbmc.org 6 | * Copyright (C) 2008-2009 Andrej Stepanchuk 7 | * Copyright (C) 2009-2010 Howard Chu 8 | * 9 | * This file is part of librtmp. 10 | * 11 | * librtmp is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU Lesser General Public License as 13 | * published by the Free Software Foundation; either version 2.1, 14 | * or (at your option) any later version. 15 | * 16 | * librtmp is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Lesser General Public License 22 | * along with librtmp see the file COPYING. If not, write to 23 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24 | * Boston, MA 02110-1301, USA. 25 | * http://www.gnu.org/copyleft/lgpl.html 26 | */ 27 | 28 | #if !defined(NO_CRYPTO) && !defined(CRYPTO) 29 | #define CRYPTO 30 | #endif 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include "amf.h" 37 | 38 | #ifdef __cplusplus 39 | extern "C" 40 | { 41 | #endif 42 | 43 | #define RTMP_LIB_VERSION 0x020300 /* 2.3 */ 44 | 45 | #define RTMP_FEATURE_HTTP 0x01 46 | #define RTMP_FEATURE_ENC 0x02 47 | #define RTMP_FEATURE_SSL 0x04 48 | #define RTMP_FEATURE_MFP 0x08 /* not yet supported */ 49 | #define RTMP_FEATURE_WRITE 0x10 /* publish, not play */ 50 | #define RTMP_FEATURE_HTTP2 0x20 /* server-side rtmpt */ 51 | 52 | #define RTMP_PROTOCOL_UNDEFINED -1 53 | #define RTMP_PROTOCOL_RTMP 0 54 | #define RTMP_PROTOCOL_RTMPE RTMP_FEATURE_ENC 55 | #define RTMP_PROTOCOL_RTMPT RTMP_FEATURE_HTTP 56 | #define RTMP_PROTOCOL_RTMPS RTMP_FEATURE_SSL 57 | #define RTMP_PROTOCOL_RTMPTE (RTMP_FEATURE_HTTP|RTMP_FEATURE_ENC) 58 | #define RTMP_PROTOCOL_RTMPTS (RTMP_FEATURE_HTTP|RTMP_FEATURE_SSL) 59 | #define RTMP_PROTOCOL_RTMFP RTMP_FEATURE_MFP 60 | 61 | #define RTMP_DEFAULT_CHUNKSIZE 128 62 | 63 | /* needs to fit largest number of bytes recv() may return */ 64 | #define RTMP_BUFFER_CACHE_SIZE (16*1024) 65 | 66 | #define RTMP_CHANNELS 65600 67 | 68 | extern const char RTMPProtocolStringsLower[][7]; 69 | extern const AVal RTMP_DefaultFlashVer; 70 | extern int RTMP_ctrlC; 71 | 72 | uint32_t RTMP_GetTime(void); 73 | 74 | /* RTMP_PACKET_TYPE_... 0x00 */ 75 | #define RTMP_PACKET_TYPE_CHUNK_SIZE 0x01 76 | /* RTMP_PACKET_TYPE_... 0x02 */ 77 | #define RTMP_PACKET_TYPE_BYTES_READ_REPORT 0x03 78 | #define RTMP_PACKET_TYPE_CONTROL 0x04 79 | #define RTMP_PACKET_TYPE_SERVER_BW 0x05 80 | #define RTMP_PACKET_TYPE_CLIENT_BW 0x06 81 | /* RTMP_PACKET_TYPE_... 0x07 */ 82 | #define RTMP_PACKET_TYPE_AUDIO 0x08 83 | #define RTMP_PACKET_TYPE_VIDEO 0x09 84 | /* RTMP_PACKET_TYPE_... 0x0A */ 85 | /* RTMP_PACKET_TYPE_... 0x0B */ 86 | /* RTMP_PACKET_TYPE_... 0x0C */ 87 | /* RTMP_PACKET_TYPE_... 0x0D */ 88 | /* RTMP_PACKET_TYPE_... 0x0E */ 89 | #define RTMP_PACKET_TYPE_FLEX_STREAM_SEND 0x0F 90 | #define RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT 0x10 91 | #define RTMP_PACKET_TYPE_FLEX_MESSAGE 0x11 92 | #define RTMP_PACKET_TYPE_INFO 0x12 93 | #define RTMP_PACKET_TYPE_SHARED_OBJECT 0x13 94 | #define RTMP_PACKET_TYPE_INVOKE 0x14 95 | /* RTMP_PACKET_TYPE_... 0x15 */ 96 | #define RTMP_PACKET_TYPE_FLASH_VIDEO 0x16 97 | 98 | #define RTMP_MAX_HEADER_SIZE 18 99 | 100 | #define RTMP_PACKET_SIZE_LARGE 0 101 | #define RTMP_PACKET_SIZE_MEDIUM 1 102 | #define RTMP_PACKET_SIZE_SMALL 2 103 | #define RTMP_PACKET_SIZE_MINIMUM 3 104 | 105 | typedef struct RTMPChunk 106 | { 107 | int c_headerSize; 108 | int c_chunkSize; 109 | char *c_chunk; 110 | char c_header[RTMP_MAX_HEADER_SIZE]; 111 | } RTMPChunk; 112 | 113 | typedef struct RTMPPacket 114 | { 115 | uint8_t m_headerType; 116 | uint8_t m_packetType; 117 | uint8_t m_hasAbsTimestamp; /* timestamp absolute or relative? */ 118 | int m_nChannel; 119 | uint32_t m_nTimeStamp; /* timestamp */ 120 | int32_t m_nInfoField2; /* last 4 bytes in a long header */ 121 | uint32_t m_nBodySize; 122 | uint32_t m_nBytesRead; 123 | RTMPChunk *m_chunk; 124 | char *m_body; 125 | } RTMPPacket; 126 | 127 | typedef struct RTMPSockBuf 128 | { 129 | int sb_socket; 130 | int sb_size; /* number of unprocessed bytes in buffer */ 131 | char *sb_start; /* pointer into sb_pBuffer of next byte to process */ 132 | char sb_buf[RTMP_BUFFER_CACHE_SIZE]; /* data read from socket */ 133 | int sb_timedout; 134 | void *sb_ssl; 135 | } RTMPSockBuf; 136 | 137 | void RTMPPacket_Reset(RTMPPacket *p); 138 | void RTMPPacket_Dump(RTMPPacket *p); 139 | int RTMPPacket_Alloc(RTMPPacket *p, int nSize); 140 | void RTMPPacket_Free(RTMPPacket *p); 141 | 142 | #define RTMPPacket_IsReady(a) ((a)->m_nBytesRead == (a)->m_nBodySize) 143 | 144 | typedef struct RTMP_LNK 145 | { 146 | AVal hostname; 147 | AVal sockshost; 148 | 149 | AVal playpath0; /* parsed from URL */ 150 | AVal playpath; /* passed in explicitly */ 151 | AVal tcUrl; 152 | AVal swfUrl; 153 | AVal pageUrl; 154 | AVal app; 155 | AVal auth; 156 | AVal flashVer; 157 | AVal subscribepath; 158 | AVal usherToken; 159 | AVal token; 160 | AMFObject extras; 161 | int edepth; 162 | 163 | int seekTime; 164 | int stopTime; 165 | 166 | #define RTMP_LF_AUTH 0x0001 /* using auth param */ 167 | #define RTMP_LF_LIVE 0x0002 /* stream is live */ 168 | #define RTMP_LF_SWFV 0x0004 /* do SWF verification */ 169 | #define RTMP_LF_PLST 0x0008 /* send playlist before play */ 170 | #define RTMP_LF_BUFX 0x0010 /* toggle stream on BufferEmpty msg */ 171 | #define RTMP_LF_FTCU 0x0020 /* free tcUrl on close */ 172 | int lFlags; 173 | 174 | int swfAge; 175 | 176 | int protocol; 177 | int timeout; /* connection timeout in seconds */ 178 | 179 | unsigned short socksport; 180 | unsigned short port; 181 | 182 | #ifdef CRYPTO 183 | #define RTMP_SWF_HASHLEN 32 184 | void *dh; /* for encryption */ 185 | void *rc4keyIn; 186 | void *rc4keyOut; 187 | 188 | uint32_t SWFSize; 189 | uint8_t SWFHash[RTMP_SWF_HASHLEN]; 190 | char SWFVerificationResponse[RTMP_SWF_HASHLEN+10]; 191 | #endif 192 | } RTMP_LNK; 193 | 194 | /* state for read() wrapper */ 195 | typedef struct RTMP_READ 196 | { 197 | char *buf; 198 | char *bufpos; 199 | unsigned int buflen; 200 | uint32_t timestamp; 201 | uint8_t dataType; 202 | uint8_t flags; 203 | #define RTMP_READ_HEADER 0x01 204 | #define RTMP_READ_RESUME 0x02 205 | #define RTMP_READ_NO_IGNORE 0x04 206 | #define RTMP_READ_GOTKF 0x08 207 | #define RTMP_READ_GOTFLVK 0x10 208 | #define RTMP_READ_SEEKING 0x20 209 | int8_t status; 210 | #define RTMP_READ_COMPLETE -3 211 | #define RTMP_READ_ERROR -2 212 | #define RTMP_READ_EOF -1 213 | #define RTMP_READ_IGNORE 0 214 | 215 | /* if bResume == TRUE */ 216 | uint8_t initialFrameType; 217 | uint32_t nResumeTS; 218 | char *metaHeader; 219 | char *initialFrame; 220 | uint32_t nMetaHeaderSize; 221 | uint32_t nInitialFrameSize; 222 | uint32_t nIgnoredFrameCounter; 223 | uint32_t nIgnoredFlvFrameCounter; 224 | } RTMP_READ; 225 | 226 | typedef struct RTMP_METHOD 227 | { 228 | AVal name; 229 | int num; 230 | } RTMP_METHOD; 231 | 232 | typedef struct RTMP 233 | { 234 | int m_inChunkSize; 235 | int m_outChunkSize; 236 | int m_nBWCheckCounter; 237 | int m_nBytesIn; 238 | int m_nBytesInSent; 239 | int m_nBufferMS; 240 | int m_stream_id; /* returned in _result from createStream */ 241 | int m_mediaChannel; 242 | uint32_t m_mediaStamp; 243 | uint32_t m_pauseStamp; 244 | int m_pausing; 245 | int m_nServerBW; 246 | int m_nClientBW; 247 | uint8_t m_nClientBW2; 248 | uint8_t m_bPlaying; 249 | uint8_t m_bSendEncoding; 250 | uint8_t m_bSendCounter; 251 | 252 | int m_numInvokes; 253 | int m_numCalls; 254 | RTMP_METHOD *m_methodCalls; /* remote method calls queue */ 255 | 256 | RTMPPacket *m_vecChannelsIn[RTMP_CHANNELS]; 257 | RTMPPacket *m_vecChannelsOut[RTMP_CHANNELS]; 258 | int m_channelTimestamp[RTMP_CHANNELS]; /* abs timestamp of last packet */ 259 | 260 | double m_fAudioCodecs; /* audioCodecs for the connect packet */ 261 | double m_fVideoCodecs; /* videoCodecs for the connect packet */ 262 | double m_fEncoding; /* AMF0 or AMF3 */ 263 | 264 | double m_fDuration; /* duration of stream in seconds */ 265 | 266 | int m_msgCounter; /* RTMPT stuff */ 267 | int m_polling; 268 | int m_resplen; 269 | int m_unackd; 270 | AVal m_clientID; 271 | 272 | RTMP_READ m_read; 273 | RTMPPacket m_write; 274 | RTMPSockBuf m_sb; 275 | RTMP_LNK Link; 276 | } RTMP; 277 | 278 | int RTMP_ParseURL(const char *url, int *protocol, AVal *host, 279 | unsigned int *port, AVal *playpath, AVal *app); 280 | 281 | void RTMP_ParsePlaypath(AVal *in, AVal *out); 282 | void RTMP_SetBufferMS(RTMP *r, int size); 283 | void RTMP_UpdateBufferMS(RTMP *r); 284 | 285 | int RTMP_SetOpt(RTMP *r, const AVal *opt, AVal *arg); 286 | int RTMP_SetupURL(RTMP *r, char *url); 287 | void RTMP_SetupStream(RTMP *r, int protocol, 288 | AVal *hostname, 289 | unsigned int port, 290 | AVal *sockshost, 291 | AVal *playpath, 292 | AVal *tcUrl, 293 | AVal *swfUrl, 294 | AVal *pageUrl, 295 | AVal *app, 296 | AVal *auth, 297 | AVal *swfSHA256Hash, 298 | uint32_t swfSize, 299 | AVal *flashVer, 300 | AVal *subscribepath, 301 | AVal *usherToken, 302 | int dStart, 303 | int dStop, int bLiveStream, long int timeout); 304 | 305 | int RTMP_Connect(RTMP *r, RTMPPacket *cp); 306 | struct sockaddr; 307 | int RTMP_Connect0(RTMP *r, struct sockaddr *svc); 308 | int RTMP_Connect1(RTMP *r, RTMPPacket *cp); 309 | int RTMP_Serve(RTMP *r); 310 | 311 | int RTMP_ReadPacket(RTMP *r, RTMPPacket *packet); 312 | int RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue); 313 | int RTMP_SendChunk(RTMP *r, RTMPChunk *chunk); 314 | int RTMP_IsConnected(RTMP *r); 315 | int RTMP_Socket(RTMP *r); 316 | int RTMP_IsTimedout(RTMP *r); 317 | double RTMP_GetDuration(RTMP *r); 318 | int RTMP_ToggleStream(RTMP *r); 319 | 320 | int RTMP_ConnectStream(RTMP *r, int seekTime); 321 | int RTMP_ReconnectStream(RTMP *r, int seekTime); 322 | void RTMP_DeleteStream(RTMP *r); 323 | int RTMP_GetNextMediaPacket(RTMP *r, RTMPPacket *packet); 324 | int RTMP_ClientPacket(RTMP *r, RTMPPacket *packet); 325 | 326 | void RTMP_Init(RTMP *r); 327 | void RTMP_Close(RTMP *r); 328 | RTMP *RTMP_Alloc(void); 329 | void RTMP_Free(RTMP *r); 330 | void RTMP_EnableWrite(RTMP *r); 331 | 332 | int RTMP_LibVersion(void); 333 | void RTMP_UserInterrupt(void); /* user typed Ctrl-C */ 334 | 335 | int RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, 336 | unsigned int nTime); 337 | 338 | /* caller probably doesn't know current timestamp, should 339 | * just use RTMP_Pause instead 340 | */ 341 | int RTMP_SendPause(RTMP *r, int DoPause, int dTime); 342 | int RTMP_Pause(RTMP *r, int DoPause); 343 | 344 | int RTMP_FindFirstMatchingProperty(AMFObject *obj, const AVal *name, 345 | AMFObjectProperty * p); 346 | 347 | int RTMPSockBuf_Fill(RTMPSockBuf *sb); 348 | int RTMPSockBuf_Send(RTMPSockBuf *sb, const char *buf, int len); 349 | int RTMPSockBuf_Close(RTMPSockBuf *sb); 350 | 351 | int RTMP_SendCreateStream(RTMP *r); 352 | int RTMP_SendSeek(RTMP *r, int dTime); 353 | int RTMP_SendServerBW(RTMP *r); 354 | int RTMP_SendClientBW(RTMP *r); 355 | void RTMP_DropRequest(RTMP *r, int i, int freeit); 356 | int RTMP_Read(RTMP *r, char *buf, int size); 357 | int RTMP_Write(RTMP *r, const char *buf, int size); 358 | 359 | /* hashswf.c */ 360 | int RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash, 361 | int age); 362 | 363 | #ifdef __cplusplus 364 | }; 365 | #endif 366 | 367 | #endif 368 | -------------------------------------------------------------------------------- /librtmp/rtmp_sys.h: -------------------------------------------------------------------------------- 1 | #ifndef __RTMP_SYS_H__ 2 | #define __RTMP_SYS_H__ 3 | /* 4 | * Copyright (C) 2010 Howard Chu 5 | * 6 | * This file is part of librtmp. 7 | * 8 | * librtmp is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU Lesser General Public License as 10 | * published by the Free Software Foundation; either version 2.1, 11 | * or (at your option) any later version. 12 | * 13 | * librtmp is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public License 19 | * along with librtmp see the file COPYING. If not, write to 20 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 | * Boston, MA 02110-1301, USA. 22 | * http://www.gnu.org/copyleft/lgpl.html 23 | */ 24 | 25 | #ifdef _WIN32 26 | 27 | #include 28 | #include 29 | 30 | #ifdef _MSC_VER /* MSVC */ 31 | #define snprintf _snprintf 32 | #define strcasecmp stricmp 33 | #define strncasecmp strnicmp 34 | #define vsnprintf _vsnprintf 35 | #endif 36 | 37 | #define GetSockError() WSAGetLastError() 38 | #define SetSockError(e) WSASetLastError(e) 39 | #define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e) 40 | #define EWOULDBLOCK WSAETIMEDOUT /* we don't use nonblocking, but we do use timeouts */ 41 | #define sleep(n) Sleep(n*1000) 42 | #define msleep(n) Sleep(n) 43 | #define SET_RCVTIMEO(tv,s) int tv = s*1000 44 | #else /* !_WIN32 */ 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #define GetSockError() errno 54 | #define SetSockError(e) errno = e 55 | #undef closesocket 56 | #define closesocket(s) close(s) 57 | #define msleep(n) usleep(n*1000) 58 | #define SET_RCVTIMEO(tv,s) struct timeval tv = {s,0} 59 | #endif 60 | 61 | #include "rtmp.h" 62 | 63 | #ifdef USE_POLARSSL 64 | #include 65 | #include 66 | #include 67 | typedef struct tls_ctx { 68 | havege_state hs; 69 | ssl_session ssn; 70 | } tls_ctx; 71 | #define TLS_CTX tls_ctx * 72 | #define TLS_client(ctx,s) s = malloc(sizeof(ssl_context)); ssl_init(s);\ 73 | ssl_set_endpoint(s, SSL_IS_CLIENT); ssl_set_authmode(s, SSL_VERIFY_NONE);\ 74 | ssl_set_rng(s, havege_rand, &ctx->hs);\ 75 | ssl_set_ciphersuites(s, ssl_default_ciphersuites);\ 76 | ssl_set_session(s, 1, 600, &ctx->ssn) 77 | #define TLS_setfd(s,fd) ssl_set_bio(s, net_recv, &fd, net_send, &fd) 78 | #define TLS_connect(s) ssl_handshake(s) 79 | #define TLS_read(s,b,l) ssl_read(s,(unsigned char *)b,l) 80 | #define TLS_write(s,b,l) ssl_write(s,(unsigned char *)b,l) 81 | #define TLS_shutdown(s) ssl_close_notify(s) 82 | #define TLS_close(s) ssl_free(s); free(s) 83 | 84 | #elif defined(USE_GNUTLS) 85 | #include 86 | typedef struct tls_ctx { 87 | gnutls_certificate_credentials_t cred; 88 | gnutls_priority_t prios; 89 | } tls_ctx; 90 | #define TLS_CTX tls_ctx * 91 | #define TLS_client(ctx,s) gnutls_init((gnutls_session_t *)(&s), GNUTLS_CLIENT); gnutls_priority_set(s, ctx->prios); gnutls_credentials_set(s, GNUTLS_CRD_CERTIFICATE, ctx->cred) 92 | #define TLS_setfd(s,fd) gnutls_transport_set_ptr(s, (gnutls_transport_ptr_t)(long)fd) 93 | #define TLS_connect(s) gnutls_handshake(s) 94 | #define TLS_read(s,b,l) gnutls_record_recv(s,b,l) 95 | #define TLS_write(s,b,l) gnutls_record_send(s,b,l) 96 | #define TLS_shutdown(s) gnutls_bye(s, GNUTLS_SHUT_RDWR) 97 | #define TLS_close(s) gnutls_deinit(s) 98 | 99 | #else /* USE_OPENSSL */ 100 | #define TLS_CTX SSL_CTX * 101 | #define TLS_client(ctx,s) s = SSL_new(ctx) 102 | #define TLS_setfd(s,fd) SSL_set_fd(s,fd) 103 | #define TLS_connect(s) SSL_connect(s) 104 | #define TLS_read(s,b,l) SSL_read(s,b,l) 105 | #define TLS_write(s,b,l) SSL_write(s,b,l) 106 | #define TLS_shutdown(s) SSL_shutdown(s) 107 | #define TLS_close(s) SSL_free(s) 108 | 109 | #endif 110 | #endif 111 | --------------------------------------------------------------------------------