You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mod_client_proxy.lua 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. if module:get_host_type() ~= "component" then
  2. error("proxy_component should be loaded as component", 0);
  3. end
  4. local jid_split = require "util.jid".split;
  5. local jid_bare = require "util.jid".bare;
  6. local jid_prep = require "util.jid".prep;
  7. local st = require "util.stanza";
  8. local array = require "util.array";
  9. local target_address = module:get_option_string("target_address");
  10. sessions = array{};
  11. local sessions = sessions;
  12. local function handle_target_presence(stanza)
  13. local type = stanza.attr.type;
  14. module:log("debug", "received presence from destination: %s", type)
  15. local _, _, resource = jid_split(stanza.attr.from);
  16. if type == "error" then
  17. -- drop all known sessions
  18. for k in pairs(sessions) do
  19. sessions[k] = nil
  20. end
  21. module:log(
  22. "debug",
  23. "received error presence, dropping all target sessions",
  24. resource
  25. )
  26. elseif type == "unavailable" then
  27. for k in pairs(sessions) do
  28. if sessions[k] == resource then
  29. sessions[k] = nil
  30. module:log(
  31. "debug",
  32. "dropped target session: %s",
  33. resource
  34. )
  35. break
  36. end
  37. end
  38. elseif not type then
  39. -- available
  40. local found = false;
  41. for k in pairs(sessions) do
  42. if sessions[k] == resource then
  43. found = true;
  44. break
  45. end
  46. end
  47. if not found then
  48. module:log(
  49. "debug",
  50. "registered new target session: %s",
  51. resource
  52. )
  53. sessions:push(resource)
  54. end
  55. end
  56. end
  57. local function handle_from_target(stanza)
  58. local type = stanza.attr.type
  59. module:log(
  60. "debug",
  61. "non-presence stanza from target: name = %s, type = %s",
  62. stanza.name,
  63. type
  64. )
  65. if stanza.name == "iq" then
  66. if type == "error" or type == "result" then
  67. -- de-NAT message
  68. local _, _, denatted_to_unprepped = jid_split(stanza.attr.to);
  69. local denatted_to = jid_prep(denatted_to_unprepped);
  70. if not denatted_to then
  71. module:log(
  72. "debug",
  73. "cannot de-NAT stanza, invalid to: %s",
  74. denatted_to_unprepped
  75. )
  76. return
  77. end
  78. local denatted_from = module:get_host();
  79. module:log(
  80. "debug",
  81. "de-NAT-ed stanza: from: %s -> %s, to: %s -> %s",
  82. stanza.attr.from,
  83. denatted_from,
  84. stanza.attr.to,
  85. denatted_to
  86. )
  87. stanza.attr.from = denatted_from
  88. stanza.attr.to = denatted_to
  89. module:send(stanza)
  90. else
  91. -- FIXME: we don’t support NATing outbund requests atm.
  92. module:send(st.error_reply(stanza, "cancel", "feature-not-implemented"))
  93. end
  94. elseif stanza.name == "message" then
  95. -- not implemented yet, we need a way to ensure that routing doesn’t
  96. -- break
  97. module:send(st.error_reply(stanza, "cancel", "feature-not-implemented"))
  98. end
  99. end
  100. local function handle_to_target(stanza)
  101. local type = stanza.attr.type;
  102. module:log(
  103. "debug",
  104. "stanza to target: name = %s, type = %s",
  105. stanza.name, type
  106. )
  107. if stanza.name == "presence" then
  108. if type ~= "error" then
  109. module:send(st.error_reply(stanza, "cancel", "bad-request"))
  110. return
  111. end
  112. elseif stanza.name == "iq" then
  113. if type == "get" or type == "set" then
  114. if #sessions == 0 then
  115. -- no sessions available to send to
  116. module:log("debug", "no sessions to send to!")
  117. module:send(st.error_reply(stanza, "cancel", "service-unavailable"))
  118. return
  119. end
  120. -- find a target session
  121. local target_session = sessions:random()
  122. local target = target_address .. "/" .. target_session
  123. -- encode sender JID in resource
  124. local natted_from = module:get_host() .. "/" .. stanza.attr.from;
  125. module:log(
  126. "debug",
  127. "NAT-ed stanza: from: %s -> %s, to: %s -> %s",
  128. stanza.attr.from,
  129. natted_from,
  130. stanza.attr.to,
  131. target
  132. )
  133. stanza.attr.from = natted_from
  134. stanza.attr.to = target
  135. module:send(stanza)
  136. end
  137. -- FIXME: handle and forward result/error correctly
  138. elseif stanza.name == "message" then
  139. -- not implemented yet, we need a way to ensure that routing doesn’t
  140. -- break
  141. module:send(st.error_reply(stanza, "cancel", "feature-not-implemented"))
  142. end
  143. end
  144. local function stanza_handler(event)
  145. local origin, stanza = event.origin, event.stanza
  146. module:log("debug", "received stanza from %s session", origin.type)
  147. local bare_from = jid_bare(stanza.attr.from);
  148. local _, _, to = jid_split(stanza.attr.to);
  149. if bare_from == target_address then
  150. -- from our target, to whom?
  151. if not to then
  152. -- directly to component
  153. if stanza.name == "presence" then
  154. handle_target_presence(stanza)
  155. else
  156. module:send(st.error_reply(stanza, "cancel", "bad-request"))
  157. return true
  158. end
  159. else
  160. -- to someone else
  161. handle_from_target(stanza)
  162. end
  163. else
  164. handle_to_target(stanza)
  165. end
  166. return true
  167. end
  168. module:hook("iq/bare", stanza_handler, -1);
  169. module:hook("message/bare", stanza_handler, -1);
  170. module:hook("presence/bare", stanza_handler, -1);
  171. module:hook("iq/full", stanza_handler, -1);
  172. module:hook("message/full", stanza_handler, -1);
  173. module:hook("presence/full", stanza_handler, -1);
  174. module:hook("iq/host", stanza_handler, -1);
  175. module:hook("message/host", stanza_handler, -1);
  176. module:hook("presence/host", stanza_handler, -1);
  177. module:log("debug", "loaded proxy on %s", module:get_host())
  178. subscription_request = st.presence({
  179. type = "subscribe",
  180. to = target_address,
  181. from = module:get_host()}
  182. )
  183. module:send(subscription_request)