Skip to content

Commit 1ea0729

Browse files
NordskogBjornskjald
authored andcommitted
Connect thrift message reworked with data from android app, consolidated with other thrift payloads
1 parent c66ac83 commit 1ea0729

File tree

4 files changed

+179
-123
lines changed

4 files changed

+179
-123
lines changed

src/mqtt/messages/Connect.ts

Lines changed: 19 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -5,137 +5,33 @@ import DeviceId from '../../types/DeviceId'
55
import MqttMessage from '../MqttMessage'
66
import { MqttConnectFlag, MqttMessageFlag } from '../MqttTypes'
77
import { MessageType } from './MessageTypes'
8+
import * as Payloads from '../payloads'
89

910
const USER_AGENT =
1011
'Facebook plugin / LIBFB-JS / [FBAN/Orca-Android;FBAV/148.0.0.5.83;FBPN/com.facebook.orca;FBLC/en_US;FBBV/26040814]'
1112

12-
enum FacebookCapFlags {
13-
FB_CP_ACKNOWLEDGED_DELIVERY = 1 << 0,
14-
FB_CP_PROCESSING_LASTACTIVE_PRESENCEINFO = 1 << 1,
15-
FB_CP_EXACT_KEEPALIVE = 1 << 2,
16-
FB_CP_REQUIRES_JSON_UNICODE_ESCAPES = 1 << 3,
17-
FB_CP_DELTA_SENT_MESSAGE_ENABLED = 1 << 4,
18-
FB_CP_USE_ENUM_TOPIC = 1 << 5,
19-
FB_CP_SUPPRESS_GETDIFF_IN_CONNECT = 1 << 6,
20-
FB_CP_USE_THRIFT_FOR_INBOX = 1 << 7,
21-
FB_CP_USE_SEND_PINGRESP = 1 << 8,
22-
FB_CP_REQUIRE_REPLAY_PROTECTION = 1 << 9,
23-
FB_CP_DATA_SAVING_MODE = 1 << 10,
24-
FB_CP_TYPING_OFF_WHEN_SENDING_MESSAGE = 1 << 11
25-
}
2613
/**
2714
* Assembles a connect messages sent just after a TLS connection is established.
2815
*/
29-
export const encodeConnectMessage = (
16+
export const encodeConnectMessage = async (
3017
tokens: AuthTokens,
3118
deviceId: DeviceId
3219
): Promise<MqttMessage> => {
33-
return new Promise<MqttMessage>((resolve, reject) => {
34-
const trans = new TBufferedTransport() as any
35-
trans.onFlush = d => {
36-
const flags =
37-
MqttConnectFlag.User |
38-
MqttConnectFlag.Pass |
39-
MqttConnectFlag.Clr |
40-
MqttConnectFlag.QoS1
41-
const message = new MqttMessage(MessageType.Connect)
42-
.setFlags(MqttMessageFlag.QoS0)
43-
.writeString('MQTToT')
44-
.writeU8(3)
45-
.writeU8(flags)
46-
.writeU16(60) // KEEP ALIVE
47-
.writeRaw(zlib.deflateSync(d))
48-
resolve(message)
49-
}
50-
51-
const proto = new TCompactProtocol(trans) as any
52-
// Write client id
53-
proto.lastFieldId_ = 0
54-
proto.writeFieldBegin('none', Thrift.Type.STRING, 1)
55-
proto.writeString(deviceId.clientId)
56-
57-
proto.lastFieldId_ = 1
58-
proto.writeFieldBegin('none', Thrift.Type.STRUCT, 4)
59-
60-
// Write user id
61-
proto.lastFieldId_ = 0
62-
proto.writeFieldBegin('none', Thrift.Type.I64, 1)
63-
proto.writeI64(tokens.uid)
64-
65-
// Write information
66-
proto.lastFieldId_ = 1
67-
proto.writeFieldBegin('none', Thrift.Type.STRING, 2)
68-
proto.writeString(USER_AGENT)
69-
70-
// Write some random int (?)
71-
proto.lastFieldId_ = 2
72-
proto.writeFieldBegin('None', Thrift.Type.I64, 3)
73-
proto.writeI64(
74-
FacebookCapFlags.FB_CP_ACKNOWLEDGED_DELIVERY |
75-
FacebookCapFlags.FB_CP_PROCESSING_LASTACTIVE_PRESENCEINFO |
76-
FacebookCapFlags.FB_CP_EXACT_KEEPALIVE |
77-
FacebookCapFlags.FB_CP_DELTA_SENT_MESSAGE_ENABLED
78-
)
79-
80-
// Write some random int (?)
81-
proto.lastFieldId_ = 3
82-
proto.writeFieldBegin('None', Thrift.Type.I64, 4)
83-
proto.writeI64(26)
84-
85-
// Write some random int (?)
86-
proto.lastFieldId_ = 4
87-
proto.writeFieldBegin('None', Thrift.Type.I32, 5)
88-
proto.writeI32(1)
89-
90-
// Write no_auto_fg boolean
91-
92-
proto.lastFieldId_ = 5
93-
proto.writeFieldBegin('none', Thrift.Type.BOOL, 6)
94-
proto.writeBool(true)
95-
96-
// Write visibility state
97-
proto.lastFieldId_ = 6
98-
proto.writeFieldBegin('none', Thrift.Type.BOOL, 7)
99-
proto.writeBool(true)
100-
101-
// Write device id
102-
proto.lastFieldId_ = 7
103-
proto.writeFieldBegin('none', Thrift.Type.STRING, 8)
104-
proto.writeString(deviceId.deviceId)
105-
106-
// Write fg boolean
107-
proto.lastFieldId_ = 8
108-
proto.writeFieldBegin('none', Thrift.Type.BOOL, 9)
109-
proto.writeBool(true)
110-
111-
// nwt int
112-
proto.lastFieldId_ = 9
113-
proto.writeFieldBegin('none', Thrift.Type.I32, 10)
114-
proto.writeI32(1)
115-
116-
// nwst int
117-
proto.lastFieldId_ = 10
118-
proto.writeFieldBegin('none', Thrift.Type.I32, 11)
119-
proto.writeI32(0)
120-
121-
// write mqtt id
122-
proto.lastFieldId_ = 11
123-
proto.writeFieldBegin('none', Thrift.Type.I64, 12)
124-
proto.writeI64(deviceId.mqttId)
125-
126-
// write some random list
127-
proto.lastFieldId_ = 12
128-
proto.writeFieldBegin('none', Thrift.Type.LIST, 14)
129-
proto.writeListBegin(Thrift.Type.I32, 0) // wtf(?)
130-
proto.writeFieldStop()
131-
132-
// Write token
133-
proto.lastFieldId_ = 14
134-
proto.writeFieldBegin('none', Thrift.Type.STRING, 15)
135-
proto.writeString(tokens.access_token)
136-
proto.writeFieldStop()
137-
proto.writeStructEnd()
138-
139-
proto.flush()
140-
})
20+
const payload = new Payloads.Connect(deviceId, tokens, USER_AGENT)
21+
22+
const message = new MqttMessage()
23+
const flags =
24+
MqttConnectFlag.User |
25+
MqttConnectFlag.Pass |
26+
MqttConnectFlag.Clr |
27+
MqttConnectFlag.QoS1
28+
message.writeString('MQTToT')
29+
message.writeU8(3)
30+
message.writeU8(flags)
31+
message.writeU16(60) // KEEP ALIVE
32+
message.writeRaw(zlib.deflateSync(await Payloads.encodePayload(payload)))
33+
message.flags = 0
34+
message.type = FacebookMessageType.Connect
35+
36+
return message
14137
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import Payload from './Payload'
2+
import { Thrift, TCompactProtocol, Int64 } from 'thrift'
3+
import FacebookCapFlags from '../../types/FacebookCapFlags'
4+
import Long from 'long'
5+
import AuthTokens from '../../types/AuthTokens'
6+
import DeviceId from '../../types/DeviceId'
7+
8+
export default class ConnectRequest extends Payload {
9+
deviceId: DeviceId
10+
tokens: AuthTokens
11+
userAgent: string
12+
13+
constructor (deviceId: DeviceId, tokens: AuthTokens, userAgent: string) {
14+
super()
15+
this.deviceId = deviceId
16+
this.tokens = tokens
17+
this.userAgent = userAgent
18+
}
19+
20+
encode (proto: TCompactProtocol): Promise<void> {
21+
proto.writeFieldBegin('clientIdentifier', Thrift.Type.STRING, 1)
22+
proto.writeString(this.deviceId.clientId)
23+
24+
proto.writeFieldBegin('clientInfo', Thrift.Type.STRUCT, 4)
25+
proto.writeStructBegin('clientInfo')
26+
27+
// Write user id
28+
proto.writeFieldBegin('userId', Thrift.Type.I64, 1)
29+
proto.writeI64(new Int64(Buffer.from(Long.fromString(this.tokens.uid.toString()).toBytes())))
30+
31+
// Write information
32+
proto.writeFieldBegin('userAgent', Thrift.Type.STRING, 2)
33+
proto.writeString(this.userAgent)
34+
35+
// Write some random int (?)
36+
proto.writeFieldBegin('clientCapabilities', Thrift.Type.I64, 3)
37+
proto.writeI64(
38+
FacebookCapFlags.FB_CP_ACKNOWLEDGED_DELIVERY |
39+
FacebookCapFlags.FB_CP_PROCESSING_LASTACTIVE_PRESENCEINFO |
40+
FacebookCapFlags.FB_CP_EXACT_KEEPALIVE |
41+
FacebookCapFlags.FB_CP_LARGE_PAYLOAD_SUPPORTED |
42+
FacebookCapFlags.FB_CP_DELTA_SENT_MESSAGE_ENABLED)
43+
44+
proto.writeFieldBegin('endpointCapabilities', Thrift.Type.I64, 4)
45+
proto.writeI64(26) // 0011010 libfb default
46+
// proto.writeI64(90) // 1011010 used by app
47+
48+
// 1: ZLIB
49+
// 2: ZLIB_OPTIONAL
50+
// 3: RAW
51+
proto.writeFieldBegin('publishFormat', Thrift.Type.I32, 5)
52+
proto.writeI32(1)
53+
54+
// Write no_auto_fg boolean
55+
proto.writeFieldBegin('noAutomaticForeground', Thrift.Type.BOOL, 6)
56+
proto.writeBool(false)
57+
58+
// Write visibility state
59+
proto.writeFieldBegin('makeUserAvailableInForeground', Thrift.Type.BOOL, 7)
60+
proto.writeBool(true)
61+
62+
// Write device id
63+
proto.writeFieldBegin('deviceId', Thrift.Type.STRING, 8)
64+
proto.writeString(this.deviceId.deviceId)
65+
66+
// Write fg boolean
67+
proto.writeFieldBegin('isInitiallyForeground', Thrift.Type.BOOL, 9)
68+
proto.writeBool(true)
69+
70+
// nwt int
71+
proto.writeFieldBegin('networkType', Thrift.Type.I32, 10)
72+
proto.writeI32(1)
73+
74+
// nwst int
75+
proto.writeFieldBegin('networkSubtype', Thrift.Type.I32, 11)
76+
proto.writeI32(0)
77+
78+
// write mqtt id
79+
proto.writeFieldBegin('clientMqttSessionId', Thrift.Type.I64, 12)
80+
proto.writeI64(this.deviceId.mqttId)
81+
82+
// Topics subscribed to by the app.
83+
// libfb sends a separate subscribe message later, so this is effectively unused.
84+
// const topics = [155, 107, 150, 140, 174, 34, 59, 195, 92, 131, 75, 103, 90, 62, 98, 72, 85, 100, 86, 65, 63];
85+
const topics = []
86+
proto.writeFieldBegin('subscribeTopics', Thrift.Type.LIST, 14)
87+
proto.writeListBegin(Thrift.Type.I32, topics.length)
88+
{
89+
for (const topic of topics) {
90+
proto.writeI32(topic)
91+
}
92+
}
93+
94+
// Meaning of value not known
95+
proto.writeFieldBegin('clientType', Thrift.Type.STRING, 15)
96+
proto.writeString('')
97+
98+
// Meaning of value not known
99+
proto.writeFieldBegin('regionPreference', Thrift.Type.STRING, 19)
100+
proto.writeString('ATN')
101+
102+
// Meaning of value not known
103+
proto.writeFieldBegin('deviceSecret', Thrift.Type.STRING, 20)
104+
proto.writeString('')
105+
106+
// Meaning of value not known
107+
proto.writeFieldBegin('clientStack', Thrift.Type.BYTE, 21)
108+
proto.writeByte(4)
109+
110+
// Meaning of value not known
111+
proto.writeFieldBegin('networkTypeInfo', Thrift.Type.I32, 27)
112+
proto.writeI32(7)
113+
114+
// End of object
115+
proto.writeByte(0)
116+
117+
proto.writeStructEnd()
118+
119+
proto.writeFieldBegin('password', Thrift.Type.STRING, 5)
120+
proto.writeString(this.tokens.access_token)
121+
122+
proto.writeFieldBegin('combinedPublishes', Thrift.Type.LIST, 8)
123+
proto.writeListBegin(Thrift.Type.STRUCT, 0)
124+
125+
proto.writeFieldBegin('phpOverride', Thrift.Type.STRUCT, 11)
126+
proto.writeStructBegin('phpOverride')
127+
128+
proto.writeFieldBegin('port', Thrift.Type.I32, 2)
129+
proto.writeI32(0)
130+
131+
// End of object
132+
proto.writeByte(0)
133+
134+
proto.writeStructEnd()
135+
136+
proto.writeByte(0)
137+
138+
return null
139+
}
140+
}

src/mqtt/payloads/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Payload from './Payload'
22

33
export { Payload }
4+
export { default as Connect } from './ConnectPayload'
45
export { default as PresenceState } from './PresenceStatePayload'
56
export { default as ReadReceipt } from './ReadReceiptPayload'
67
export { default as TypingState } from './TypingStatePayload'

src/types/FacebookCapFlags.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
enum FacebookCapFlags {
2+
FB_CP_ACKNOWLEDGED_DELIVERY = 1 << 0,
3+
FB_CP_PROCESSING_LASTACTIVE_PRESENCEINFO = 1 << 1,
4+
FB_CP_EXACT_KEEPALIVE = 1 << 2,
5+
FB_CP_REQUIRES_JSON_UNICODE_ESCAPES = 1 << 3,
6+
FB_CP_DELTA_SENT_MESSAGE_ENABLED = 1 << 4,
7+
FB_CP_USE_ENUM_TOPIC = 1 << 5,
8+
FB_CP_SUPPRESS_GETDIFF_IN_CONNECT = 1 << 6,
9+
FB_CP_USE_THRIFT_FOR_INBOX = 1 << 7,
10+
FB_CP_USE_SEND_PINGRESP = 1 << 8,
11+
FB_CP_REQUIRE_REPLAY_PROTECTION = 1 << 9,
12+
FB_CP_DATA_SAVING_MODE = 1 << 10,
13+
FB_CP_TYPING_OFF_WHEN_SENDING_MESSAGE = 1 << 11,
14+
FB_CP_PERMISSION_USER_AUTH_CODE = 1 << 12,
15+
FB_CP_FBNS_EXPLICIT_DELIVERY_ACK = 1 << 13,
16+
FB_CP_LARGE_PAYLOAD_SUPPORTED = 1 << 14
17+
}
18+
19+
export default FacebookCapFlags

0 commit comments

Comments
 (0)