1 /*
2  * Copyright 2018, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "wifi_command.h"
18 
19 #include "../bridge.h"
20 #include "../utils.h"
21 
22 #include <cutils/properties.h>
23 #include <errno.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 static const char kHostApdStubFile[] = "/vendor/etc/simulated_hostapd.conf";
29 static const char kHostApdConfFile[] = "/data/vendor/wifi/hostapd/hostapd.conf";
30 
31 static const char kControlRestartProperty[] = "ctl.restart";
32 static const char kHostApdServiceName[] = "emu_hostapd";
33 
34 static const char kIfNamePrefix[] = "wlan1_";
35 
36 class File {
37 public:
File(FILE * file)38     explicit File(FILE* file) : mFile(file) {}
~File()39     ~File() {
40         if (mFile) {
41             fclose(mFile);
42         }
43     }
44 
get()45     FILE* get() { return mFile; }
46 
operator !() const47     bool operator!() const { return mFile == nullptr; }
48 private:
49     FILE* mFile;
50 };
51 
52 class Fd {
53 public:
Fd(int fd)54     explicit Fd(int fd) : mFd(fd) {}
~Fd()55     ~Fd() {
56         if (mFd != -1) {
57             ::close(mFd);
58             mFd = -1;
59         }
60     }
61 
get() const62     int get() const { return mFd; }
63 
64 private:
65     int mFd;
66 };
67 
WifiCommand(Bridge & bridge)68 WifiCommand::WifiCommand(Bridge& bridge)
69     : mBridge(bridge)
70     , mLowestInterfaceNumber(1) {
71     readConfig();
72 }
73 
onCommand(const char *,const char * args)74 Result WifiCommand::onCommand(const char* /*command*/, const char* args) {
75     const char* divider = ::strchr(args, ' ');
76     if (divider == nullptr) {
77         // Unknown command, every command needs an argument
78         return Result::error("Invalid wifi command '%s'", args);
79     }
80 
81     std::string subCommand(args, divider);
82     if (subCommand.empty()) {
83         return Result::error("Empty wifi command");
84     }
85 
86     std::vector<std::string> subArgs = explode(divider + 1, ' ');
87     if (subArgs.empty()) {
88         // All of these commands require sub arguments
89         return Result::error("Missing argument to command '%s'",
90                              subCommand.c_str());
91     }
92 
93     if (subCommand == "add") {
94         return onAdd(subArgs);
95     } else if (subCommand == "block") {
96         return onBlock(subArgs);
97     } else if (subCommand == "unblock") {
98         return onUnblock(subArgs);
99     } else {
100         return Result::error("Unknown wifi command '%s'", subCommand.c_str());
101     }
102 }
103 
readConfig()104 void WifiCommand::readConfig() {
105 }
106 
writeConfig()107 Result WifiCommand::writeConfig() {
108     File in(fopen(kHostApdStubFile, "r"));
109     if (!in) {
110         return Result::error("Config failure: could not open template: %s",
111                              strerror(errno));
112     }
113 
114     File out(fopen(kHostApdConfFile, "w"));
115     if (!out) {
116         return Result::error("Config failure: could not open target: %s",
117                              strerror(errno));
118     }
119 
120     char buffer[32768];
121     while (!feof(in.get())) {
122         size_t bytesRead = fread(buffer, 1, sizeof(buffer), in.get());
123         if (bytesRead != sizeof(buffer) && ferror(in.get())) {
124             return Result::error("Config failure: Error reading template: %s",
125                                  strerror(errno));
126         }
127 
128         size_t bytesWritten = fwrite(buffer, 1, bytesRead, out.get());
129         if (bytesWritten != bytesRead) {
130             return Result::error("Config failure: Error writing target: %s",
131                                  strerror(errno));
132         }
133     }
134     fprintf(out.get(), "\n\n");
135 
136     for (const auto& ap : mAccessPoints) {
137         fprintf(out.get(), "bss=%s\n", ap.second.ifName.c_str());
138         fprintf(out.get(), "ssid=%s\n", ap.second.ssid.c_str());
139         if (!ap.second.password.empty()) {
140             fprintf(out.get(), "wpa=2\n");
141             fprintf(out.get(), "wpa_key_mgmt=WPA-PSK\n");
142             fprintf(out.get(), "rsn_pairwise=CCMP\n");
143             fprintf(out.get(), "wpa_passphrase=%s\n", ap.second.password.c_str());
144         }
145         fprintf(out.get(), "\n");
146     }
147     return Result::success();
148 }
149 
triggerHostApd()150 Result WifiCommand::triggerHostApd() {
151     property_set(kControlRestartProperty, kHostApdServiceName);
152     return Result::success();
153 }
154 
onAdd(const std::vector<std::string> & arguments)155 Result WifiCommand::onAdd(const std::vector<std::string>& arguments) {
156     AccessPoint& ap = mAccessPoints[arguments[0]];
157     ap.ssid = arguments[0];
158     if (arguments.size() > 1) {
159         ap.password = arguments[1];
160     } else {
161         ap.password.clear();
162     }
163     if (ap.ifName.empty()) {
164         char buffer[sizeof(kIfNamePrefix) + 10];
165         while (true) {
166             snprintf(buffer, sizeof(buffer), "%s%d",
167                      kIfNamePrefix, mLowestInterfaceNumber);
168             ap.ifName = buffer;
169             auto usedInterface = mUsedInterfaces.find(ap.ifName);
170             if (usedInterface == mUsedInterfaces.end()) {
171                 // This interface is available, use it
172                 ++mLowestInterfaceNumber;
173                 mUsedInterfaces.insert(ap.ifName);
174                 break;
175             }
176             // The interface name was alread in use, try the next one
177             ++mLowestInterfaceNumber;
178         }
179     }
180     Result res = writeConfig();
181     if (!res) {
182         return res;
183     }
184     return triggerHostApd();
185 }
186 
onBlock(const std::vector<std::string> & arguments)187 Result WifiCommand::onBlock(const std::vector<std::string>& arguments) {
188     auto interface = mAccessPoints.find(arguments[0]);
189     if (interface == mAccessPoints.end()) {
190         return Result::error("Unknown SSID '%s'", arguments[0].c_str());
191     }
192     interface->second.blocked = true;
193     return mBridge.removeInterface(interface->second.ifName.c_str());
194 }
195 
onUnblock(const std::vector<std::string> & arguments)196 Result WifiCommand::onUnblock(const std::vector<std::string>& arguments) {
197     auto interface = mAccessPoints.find(arguments[0]);
198     if (interface == mAccessPoints.end()) {
199         return Result::error("Unknown SSID '%s'", arguments[0].c_str());
200     }
201     interface->second.blocked = false;
202     return mBridge.addInterface(interface->second.ifName.c_str());
203 }
204 
205