Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

index.js line 74 - /info/getLocations checks for definition of type parameter, which breaks older STB compatibility. Comments note that this may be erroneous. #2

Open
Numbski opened this issue Oct 19, 2020 · 0 comments

Comments

@Numbski
Copy link

Numbski commented Oct 19, 2020

For the sake of not repeating a whole lot of typing, I'm going to link a downstream project issue that I opened for this before I understood that the problem was actually upstream:

ToddGreenfield/homebridge-directv#17

In very, very brief terms - older set top boxes are compatible (mostly), but they do not support the type argument to /info/getLocations, and depending on how pervasive this is in the code, it could come up elsewhere. Using one of my own STBs as an example:

pi@homebridge:~/scripts/directv$ curl http://172.16.30.79:8080/info/getLocations | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   195  100   195    0     0   1772      0 --:--:-- --:--:-- --:--:--  1772
{
  "locations": [
    {
      "clientAddr": "0",
      "locationName": "LIVING ROOM"
    }
  ],
  "status": {
    "code": 200,
    "commandResult": 0,
    "msg": "OK.",
    "query": "/info/getLocations"
  }
}

Note that "type" isn't being sent. Now let's look at what directv-remote attempts to send to that receiver:

172.16.30.28.54386 > 172.16.30.76.http-alt: Flags [P.], cksum 0x9505 (incorrect -> 0x27e4), seq 1:87, ack 1, win 502, options [nop,nop,TS val 1843789388 ecr 516813839], length 86: HTTP, length: 86
	GET /info/getLocations?type=1 HTTP/1.1
	Host: 172.16.30.76:8080
	Connection: close

/info/getLocations?type=1

If I attempt to do that, here's what happens:

pi@homebridge:~/scripts/directv$ curl http://172.16.30.79:8080/info/getLocations?type=1
{"status": {
  "code": 403,
  "commandResult": 1,
  "msg": "Forbidden.Invalid URL parameter(s) found.",
  "query": "/info/getLocations?type=1"

Which makes sense, because if you poll /info/getOptions, you get this back:

pi@homebridge:~/scripts/directv$ curl http://172.16.30.79:8080/info/getOptions | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  5873  100  5873    0     0  20607      0 --:--:-- --:--:-- --:--:-- 20679
{
  "options": [
    {
      "command": "/info/getLocations",
      "description": "List of available client locations. Warning: This command may change or be disabled in the future.",
      "formControls": [],
      "urlParameters": [
        {
          "isRequired": false,
          "name": "wrapper",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "callback",
          "type": "string"
        }
      ]
    },
*snip*

It only supports wrapper and callback. Neither is required. So if we bounce back over to your code, we have this block:

    // "List of available client locations."
    // Returns an array of the networked set top boxes
    // type is an optional parameter
    //  the docs label it as 'int'
    //  only 0 and 1 aren't *Forbidden*
    //  i'm not sure what the difference is yet,
    //  but 1 shows more of my wireless Genie STBs
this.getLocations = function(type, callback){
        var path = '/info/getLocations';

        var options = {
            hostname: this.IP_ADDRESS,
            port: 8080,
            path: path
        };

        if (typeof type !== 'undefined') {
            options.path = buildQueryString(options.path, { type: type });
        }

        makeRequest(options, callbackHandler(callback));
    };

So type is "optional", but then a bit further down we check to make sure type isn't undefined. If it is, we don't do anything. If it isn't, we makeRequest(). Best I can tell, there's a line missing to call buildQueryString without the type parameter, probably inside of an else something like this (not a nodeJS dev, please correct if I have syntactical mistakes!):

        if (typeof type !== 'undefined') {
            options.path = buildQueryString(options.path, { type: type });
        }
       else {
              options.path = buildQueryString(options.path);
       }

That way we build the query string no matter what, and then makeRequest should handle the rest from there.

For the sake of completeness, here is the full output of /info/getOptions. I can make a cursory scan to see if this "type" business is present anywhere else, but it is a comparison that will be challenging without output of a more modern set of hardware. My suspicion is that on older hardware, type wasn't defined. On newer genie-era hardware type 0 is likely a standalone STB, and type 1 is a genie with child units. Either way, I think my solution solves for that. Again, not a nodeJS dev - need to go spelunking on my filesystem and see if I can apply this fix and see it work.

{
  "options": [
    {
      "command": "/info/getLocations",
      "description": "List of available client locations. Warning: This command may change or be disabled in the future.",
      "formControls": [],
      "urlParameters": [
        {
          "isRequired": false,
          "name": "wrapper",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "callback",
          "type": "string"
        }
      ]
    },
    {
      "command": "/info/getSerialNum",
      "description": "STB serial number. Warning: This command may change or be disabled in the future.",
      "formControls": [],
      "urlParameters": [
        {
          "isRequired": false,
          "name": "wrapper",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "callback",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "clientAddr",
          "type": "string"
        }
      ]
    },
    {
      "command": "/info/getVersion",
      "description": "Set-top-box and SHEF information. Warning: This command may change or be disabled in the future.",
      "formControls": [],
      "urlParameters": [
        {
          "isRequired": false,
          "name": "wrapper",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "callback",
          "type": "string"
        }
      ]
    },
    {
      "command": "/info/mode",
      "description": "Set-top-box mode. Warning: This command may change or be disabled in the future.",
      "formControls": [],
      "urlParameters": [
        {
          "isRequired": false,
          "name": "wrapper",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "callback",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "clientAddr",
          "type": "string"
        }
      ]
    },
    {
      "command": "/remote/processKey",
      "description": "Process a key request from the remote control. Warning: This command may change or be disabled in the future.",
      "formControls": [],
      "urlParameters": [
        {
          "isRequired": false,
          "name": "wrapper",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "callback",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "clientAddr",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "hold",
          "type": "string"
        },
        {
          "isRequired": true,
          "name": "key",
          "type": "string"
        }
      ]
    },
    {
      "command": "/serial/processCommand",
      "description": "Process a command request from remote control. Warning: This command may change or be disabled in the future.",
      "formControls": [],
      "urlParameters": [
        {
          "isRequired": false,
          "name": "wrapper",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "callback",
          "type": "string"
        },
        {
          "isRequired": true,
          "name": "cmd",
          "type": "hex"
        }
      ]
    },
    {
      "command": "/tv/getProgInfo",
      "description": "Program information of specified channel at current or specific time. Warning: This command may change or be disabled in the future.",
      "formControls": [],
      "urlParameters": [
        {
          "isRequired": false,
          "name": "wrapper",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "minor",
          "type": "int"
        },
        {
          "isRequired": false,
          "name": "callback",
          "type": "string"
        },
        {
          "isRequired": true,
          "name": "major",
          "type": "int"
        },
        {
          "isRequired": false,
          "name": "clientAddr",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "time",
          "type": "long"
        }
      ]
    },
    {
      "command": "/tv/getTuned",
      "description": "Information about the currently viewed program. Warning: This command may change or be disabled in the future.",
      "formControls": [],
      "urlParameters": [
        {
          "isRequired": false,
          "name": "wrapper",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "callback",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "videoWindow",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "clientAddr",
          "type": "string"
        }
      ]
    },
    {
      "command": "/tv/tune",
      "description": "Tune to a channel. Warning: This command may change or be disabled in the future.",
      "formControls": [],
      "urlParameters": [
        {
          "isRequired": false,
          "name": "wrapper",
          "type": "string"
        },
        {
          "isRequired": false,
          "name": "minor",
          "type": "int"
        },
        {
          "isRequired": false,
          "name": "callback",
          "type": "string"
        },
        {
          "isRequired": true,
          "name": "major",
          "type": "int"
        },
        {
          "isRequired": false,
          "name": "source",
          "type": "int"
        },
        {
          "isRequired": false,
          "name": "clientAddr",
          "type": "string"
        }
      ]
    }
  ],
  "status": {
    "code": 200,
    "commandResult": 0,
    "msg": "OK.",
    "query": "/info/getOptions"
  }
}

Numbski added a commit to Numbski/directv-remote that referenced this issue Oct 19, 2020
…s.getLocations.

This allows compatibility with older DirecTV STBs.  Please see
cmrust#2
and
ToddGreenfield/homebridge-directv#17
Numbski added a commit to Numbski/homebridge-directv that referenced this issue Oct 19, 2020
This is pending a fix upstream with DirecTV-remote to properly handle an
undefined "type" when calling getLocations.  Please see:

ToddGreenfield#17
and
cmrust/directv-remote#2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant