Forum Discussion

John_Reeman_470's avatar
John_Reeman_470
Icon for Nimbostratus rankNimbostratus
Jun 17, 2005

SOAP rewrite

I have SSL traffic coming into the BIGIP and being decrypted and forwarded to an HTTP server pool. Unfortunately the SOAP clients which are connecting in are entering the following in their XML 'https://epidev.xchanging.com/AcordMsgSvc/Inbox.asmx' and when the server checks this incoming SOAP request it fails with 'The header must match the value of an incoming message's HTTP Request Url if the soap receiver does not have an actor name. The header received contained "https://epidev.xchanging.com/AcordMsgSvc/Inbox.asmx" while the HTTP Request Url was "http://epidev.xchanging.com/AcordMsgSvc/Inbox.asmx".'

 

 

This is apparently a security addition in the SOAP standard and it's not possible to turn this checking off. I think the only way we can possibly get around this would be to rewrite the XML in the HTTP content data matching for 'https://' and rewrite to 'http://'.

 

 

My iRule looks like this:

 

when HTTP_REQUEST {

 

if { [string tolower [HTTP::uri] ] starts_with "/acordmsgsvc/" } {

 

if { [string first "To>https://" [HTTP::payload]] >= 0 } {

 

HTTP::payload replace [string first "To>https://" [HTTP::payload]] 16 "To>http://"

 

}

 

pool epidev-msgservers-http

 

p ersist none

 

}

 

}

 

 

It's not quite as efficient as i'd like as it searches the payload twice, but it doesn't work anyway.

 

 

I've tried:

 

 

when HTTP_REQUEST {

 

HTTP::payload replace 1 1 "THIS IS A TEST"

 

}

 

 

But that didn't make any different as far as I could tell, though I don't have a good script on the server to capture what's received unless it's in the SOAP XML standard. I think I also tried this:

 

 

when HTTP_REQUEST {

 

HTTP::collect 1024

 

}

 

when HTTP_REQUEST_DATA {

 

HTTP::payload replace 1 1 "THIS IS A TEST"

 

}

 

 

It's not clear how the HTTP_REQUEST_DATA 'when' statement gets called but I don't think it's being called at the moment.

 

 

I tried:

 

 

when HTTP_REQUEST_DATA {

 

if { [string tolower [HTTP::uri] ] starts_with "/acordmsgsvc/" } {

 

if { [string first "To>https://" [HTTP::payload]] >= 0 } {

 

HTTP::payload replace [string first "To>https://" [HTTP::payload]] 16 "To>http://"

 

}

 

pool epidev-msgservers-http

 

p ersist none

 

}

 

}

 

 

But that did even less and didn't even set the pool correctly.

 

 

Anyone done this before or point me in the right direction??!
  • What you are doing looks correct aside from the offset value in the HTTP::payload replace command

     

     

    HTTP::payload replace offset length new_string

     

     

    You are passing in a value of 16 for length when I believe it should be the length of "To>https://" which is 11.

     

     

    You cannot reliably do this in the HTTP_REQUEST method because at this point the content is not guaranteed to be received yet. You must use the HTTP::collect method in HTTP_REQUEST to ensure that the amount of content you requested is received. After a HTTP::collect method is called, the HTTP_RECEIVE_DATA event is raised and you can then access the HTTP::payload at that time.

     

     

    You can do this in one big chunk (up to [HTTP::header Content-Length]) or at little pieces at a time with successive calls to HTTP::collect.

     

     

    Another issue with your original code could be that the header isn't in the first 1024 bytes of the request. It's better to request the entire Content-Length (unless it's huge).

     

     

    In your case, I would do something like the following:

     

     

     when HTTP_REQUEST { 
       set clen [HTTP::header Content-Length] 
       if { not [info exists clen] or "" eq $clen } { 
         set clen 4096 
       } 
       HTTP::collect $clen 
     } 
      
     when HTTP_REQUEST_DATA { 
       set old_content "To>https://" 
       set new_content "To>http://" 
       set len_old_content [string length $old_content] 
      
       set offset [string first $old_content [HTTP::payload]] 
       if { $offset >= 0 } { 
         log local0. "Found '$old_content' in payload at offset $offset" 
         log local0. "Content Before: [HTTP::payload]" 
         HTTP::payload replace $offset $len_old_content $new_content 
         log local0. "Content After : [HTTP::payload]" 
       } else { 
         log local0. "Didn't find '$old_content' in payload!" 
         log local0. "Payload: [HTTP::payload]" 
       } 
     }

     

     

    The log statements will put the payload before and after the replace in the /var/log/ltm file. This should help with troubleshooting if things don't work.

     

     

    -Joe